refactor(render): 重构渲染系统,整合纹理、相机和渲染器功能

feat(render): 新增Texture类封装OpenGL纹理操作
feat(render): 新增Camera类提供2D视图投影矩阵计算
feat(render): 新增Renderer类整合图形和精灵渲染功能
refactor(window): 简化Window类实现,移除冗余功能
refactor(app): 重构Application类,简化主循环逻辑
refactor(scene): 重构场景和节点系统,适配新渲染接口
fix(assets): 修复资源管理相关头文件包含问题
chore(build): 更新xmake构建配置,移除调试宏定义
style: 统一代码格式,修复头文件合并冲突
This commit is contained in:
ChestnutYueyue 2026-02-23 01:24:27 +08:00
parent 114c5254af
commit e600730105
36 changed files with 3038 additions and 4139 deletions

View File

@ -1,113 +1,58 @@
#pragma once #pragma once
#include <extra2d/core/module.h>
#include <extra2d/core/registry.h>
#include <extra2d/core/service_locator.h> #include <extra2d/core/service_locator.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <string> #include <string>
namespace extra2d { namespace extra2d {
class GLFWWindow; class Window;
class WindowModule;
/** /**
* @brief * @brief
*/ */
class Application { class Application {
public: public:
static Application &get(); static Application& instance();
Application(const Application &) = delete; Application(const Application&) = delete;
Application &operator=(const Application &) = delete; Application& operator=(const Application&) = delete;
/** std::string name = "Extra2D App";
* @brief std::string version = "1.0.0";
*/
std::string appName = "Extra2D App";
std::string appVersion = "1.0.0";
std::string organization = "";
/**
* @brief
* @tparam T
* @tparam Args
* @return
*/
template <typename T, typename... Args> T *use(Args &&...args) {
return Registry::instance().use<T>(std::forward<Args>(args)...);
}
/**
* @brief
* @tparam T
* @return
*/
template <typename T> T *get() const { return Registry::instance().get<T>(); }
/**
* @brief
* @return true
*/
bool init(); bool init();
/**
* @brief
*/
void shutdown(); void shutdown();
/**
* @brief
*/
void run(); void run();
/**
* @brief 退
*/
void quit(); void quit();
/**
* @brief
*/
void pause();
/**
* @brief
*/
void resume();
bool paused() const { return paused_; } bool paused() const { return paused_; }
bool running() const { return running_; } bool running() const { return running_; }
Window* window() const { return window_; }
/**
* @brief
* @return
*/
GLFWWindow *window();
f32 dt() const { return dt_; } f32 dt() const { return dt_; }
f32 totalTime() const { return totalTime_; } f32 totalTime() const { return totalTime_; }
int fps() const { return fps_; } int fps() const { return fps_; }
private: private:
Application(); Application() = default;
~Application(); ~Application();
void mainLoop(); void mainLoop();
void update();
void render();
bool initialized_ = false; bool initialized_ = false;
bool running_ = false; bool running_ = false;
bool paused_ = false; bool paused_ = false;
bool shouldQuit_ = false; Window* window_ = nullptr;
f32 dt_ = 0.0f; f32 dt_ = 0.0f;
f32 totalTime_ = 0.0f; f32 totalTime_ = 0.0f;
f64 lastFrameTime_ = 0.0; u64 lastFrameTime_ = 0;
int frameCount_ = 0; int frameCount_ = 0;
f32 fpsTimer_ = 0.0f; f32 fpsTimer_ = 0.0f;
int fps_ = 0; int fps_ = 0;
}; };
#define E2D_APP() ::extra2d::Application::instance()
} // namespace extra2d } // namespace extra2d

View File

@ -6,10 +6,16 @@
// Core // Core
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
<<<<<<< HEAD #include <extra2d/core/module.h>
#include <extra2d/core/registry.h>
#include <extra2d/core/string.h> #include <extra2d/core/string.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
// Platform
#include <extra2d/platform/glfw/glfw_window.h>
#include <extra2d/platform/keys.h>
#include <extra2d/platform/window_module.h>
// Window - SDL2 + OpenGL // Window - SDL2 + OpenGL
#include <extra2d/window/window.h> #include <extra2d/window/window.h>
@ -31,22 +37,11 @@
#include <extra2d/scene/scene_manager.h> #include <extra2d/scene/scene_manager.h>
#include <extra2d/scene/shape_node.h> #include <extra2d/scene/shape_node.h>
#include <extra2d/scene/sprite.h> #include <extra2d/scene/sprite.h>
=======
#include <extra2d/core/module.h>
#include <extra2d/core/registry.h>
#include <extra2d/core/types.h>
// Platform
#include <extra2d/platform/glfw/glfw_window.h>
#include <extra2d/platform/keys.h>
#include <extra2d/platform/window_module.h>
>>>>>>> 9bf328b1dca01df84a07724394abc9a238739869
// Event // Event
#include <extra2d/event/event.h> #include <extra2d/event/event.h>
#include <extra2d/event/event_dispatcher.h> #include <extra2d/event/event_dispatcher.h>
#include <extra2d/event/event_queue.h> #include <extra2d/event/event_queue.h>
<<<<<<< HEAD
#include <extra2d/event/input_codes.h> #include <extra2d/event/input_codes.h>
// Audio // Audio
@ -55,7 +50,6 @@
// Utils // Utils
#include <extra2d/utils/data.h> #include <extra2d/utils/data.h>
#include <extra2d/utils/logger.h>
#include <extra2d/utils/random.h> #include <extra2d/utils/random.h>
#include <extra2d/utils/timer.h> #include <extra2d/utils/timer.h>
@ -64,11 +58,6 @@
#include <extra2d/spatial/spatial_hash.h> #include <extra2d/spatial/spatial_hash.h>
#include <extra2d/spatial/spatial_index.h> #include <extra2d/spatial/spatial_index.h>
#include <extra2d/spatial/spatial_manager.h> #include <extra2d/spatial/spatial_manager.h>
=======
// Utils
#include <extra2d/utils/random.h>
#include <extra2d/utils/timer.h>
// Services // Services
#include <extra2d/services/asset_service.h> #include <extra2d/services/asset_service.h>
@ -84,7 +73,6 @@
#include <extra2d/asset/asset_pack.h> #include <extra2d/asset/asset_pack.h>
#include <extra2d/asset/asset_types.h> #include <extra2d/asset/asset_types.h>
#include <extra2d/asset/data_processor.h> #include <extra2d/asset/data_processor.h>
>>>>>>> 9bf328b1dca01df84a07724394abc9a238739869
// Application // Application
#include <extra2d/app/application.h> #include <extra2d/app/application.h>

View File

@ -0,0 +1,223 @@
#pragma once
#include <extra2d/core/math_types.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/mat4x4.hpp>
namespace extra2d {
/**
* @brief 2D
*
*
*/
class Camera {
public:
Camera() = default;
~Camera() = default;
/**
* @brief
*/
void setViewport(int x, int y, int width, int height) {
viewportX_ = x;
viewportY_ = y;
viewportWidth_ = width;
viewportHeight_ = height;
dirty_ = true;
}
/**
* @brief
*/
void setPosition(const Vec2& pos) {
position_ = pos;
dirty_ = true;
}
/**
* @brief
*/
void setZoom(float zoom) {
zoom_ = zoom;
dirty_ = true;
}
/**
* @brief
*/
void setRotation(float degrees) {
rotation_ = degrees;
dirty_ = true;
}
/**
* @brief
*/
void move(const Vec2& delta) {
position_ += delta;
dirty_ = true;
}
/**
* @brief
*/
void zoom(float factor) {
zoom_ *= factor;
dirty_ = true;
}
/**
* @brief
*/
void rotate(float degrees) {
rotation_ += degrees;
dirty_ = true;
}
/**
* @brief
*/
const Vec2& position() const { return position_; }
/**
* @brief
*/
float zoom() const { return zoom_; }
/**
* @brief
*/
float rotation() const { return rotation_; }
/**
* @brief
*/
const glm::mat4& viewMatrix() const {
if (dirty_) {
updateMatrices();
}
return viewMatrix_;
}
/**
* @brief
*/
const glm::mat4& projectionMatrix() const {
if (dirty_) {
updateMatrices();
}
return projectionMatrix_;
}
/**
* @brief
*/
const glm::mat4& viewProjectionMatrix() const {
if (dirty_) {
updateMatrices();
}
return viewProjectionMatrix_;
}
/**
* @brief 便
*/
const glm::mat4& getViewProjectionMatrix() {
if (dirty_) {
updateMatrices();
}
return viewProjectionMatrix_;
}
/**
* @brief
*/
Vec2 screenToWorld(const Vec2& screenPos) const {
if (dirty_) {
updateMatrices();
}
glm::vec4 ndc(
(screenPos.x - viewportX_) / viewportWidth_ * 2.0f - 1.0f,
1.0f - (screenPos.y - viewportY_) / viewportHeight_ * 2.0f,
0.0f, 1.0f
);
glm::mat4 invVP = glm::inverse(viewProjectionMatrix_);
glm::vec4 world = invVP * ndc;
return Vec2(world.x, world.y);
}
/**
* @brief
*/
Vec2 worldToScreen(const Vec2& worldPos) const {
if (dirty_) {
updateMatrices();
}
glm::vec4 clip = viewProjectionMatrix_ * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f);
glm::vec3 ndc(clip.x / clip.w, clip.y / clip.w, clip.z / clip.w);
return Vec2(
(ndc.x + 1.0f) * 0.5f * viewportWidth_ + viewportX_,
(1.0f - ndc.y) * 0.5f * viewportHeight_ + viewportY_
);
}
/**
* @brief
*/
int viewportWidth() const { return viewportWidth_; }
/**
* @brief
*/
int viewportHeight() const { return viewportHeight_; }
private:
void updateMatrices() const {
glm::vec3 eye(position_.x, position_.y, 0.0f);
glm::vec3 center(position_.x, position_.y, -1.0f);
glm::vec3 up(0.0f, 1.0f, 0.0f);
viewMatrix_ = glm::lookAt(eye, center, up);
if (rotation_ != 0.0f) {
glm::mat4 rot = glm::rotate(glm::mat4(1.0f),
rotation_ * 3.14159265f / 180.0f,
glm::vec3(0.0f, 0.0f, 1.0f));
viewMatrix_ = rot * viewMatrix_;
}
if (zoom_ != 1.0f) {
viewMatrix_ = glm::scale(glm::mat4(1.0f),
glm::vec3(zoom_, zoom_, 1.0f)) * viewMatrix_;
}
float halfW = viewportWidth_ * 0.5f;
float halfH = viewportHeight_ * 0.5f;
projectionMatrix_ = glm::ortho(-halfW, halfW, -halfH, halfH, -1.0f, 1.0f);
viewProjectionMatrix_ = projectionMatrix_ * viewMatrix_;
dirty_ = false;
}
Vec2 position_;
float zoom_ = 1.0f;
float rotation_ = 0.0f;
int viewportX_ = 0;
int viewportY_ = 0;
int viewportWidth_ = 800;
int viewportHeight_ = 600;
mutable glm::mat4 viewMatrix_{1.0f};
mutable glm::mat4 projectionMatrix_{1.0f};
mutable glm::mat4 viewProjectionMatrix_{1.0f};
mutable bool dirty_ = true;
};
} // namespace extra2d

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <extra2d/core/color.h>
#include <extra2d/render/buffer.h> #include <extra2d/render/buffer.h>
#include <extra2d/render/render_device.h> #include <extra2d/render/render_device.h>
#include <extra2d/render/render_queue.h> #include <extra2d/render/render_queue.h>
@ -21,8 +22,10 @@ public:
/** /**
* @brief * @brief
* @param window SDL窗口指针
* @param config
*/ */
bool init(); bool init(SDL_Window *window, const RenderDeviceConfig &config = {});
/** /**
* @brief * @brief
@ -119,7 +122,7 @@ public:
/** /**
* @brief * @brief
*/ */
const RenderStats &stats() const { return stats_; } const RenderStats &stats() const;
/** /**
* @brief * @brief
@ -142,7 +145,6 @@ private:
bool initialized_ = false; bool initialized_ = false;
RenderDevice &device_ = RenderDevice::instance(); RenderDevice &device_ = RenderDevice::instance();
RenderQueue queue_; RenderQueue queue_;
RenderStats stats_;
Viewport viewport_; Viewport viewport_;
ScissorRect scissor_; ScissorRect scissor_;

View File

@ -1,7 +1,8 @@
#pragma once #pragma once
#include <extra2d/render/render_types.h> #include <SDL.h>
#include <extra2d/render/buffer.h> #include <extra2d/render/buffer.h>
#include <extra2d/render/render_types.h>
#include <extra2d/render/vao.h> #include <extra2d/render/vao.h>
#include <glad/glad.h> #include <glad/glad.h>
#include <memory> #include <memory>
@ -31,20 +32,35 @@ struct RenderCaps {
String glslVersion; String glslVersion;
}; };
/**
* @brief
*/
struct RenderDeviceConfig {
int glMajor = 4;
int glMinor = 5;
bool useES = false;
int redBits = 8;
int greenBits = 8;
int blueBits = 8;
int alphaBits = 8;
int depthBits = 24;
int stencilBits = 8;
bool doubleBuffer = true;
int msaaSamples = 0;
bool vsync = true;
};
/** /**
* @brief * @brief
*
* OpenGL 4.5 使 DSA API
*/ */
class RenderDevice { class RenderDevice {
public: public:
static RenderDevice& instance(); static RenderDevice &instance();
/** /**
* @brief * @brief OpenGL
* @return true
*/ */
bool init(); bool init(SDL_Window *window, const RenderDeviceConfig &config = {});
/** /**
* @brief * @brief
@ -59,31 +75,48 @@ public:
/** /**
* @brief * @brief
*/ */
const RenderCaps& caps() const { return caps_; } const RenderCaps &caps() const { return caps_; }
/**
* @brief SDL GL
*/
SDL_GLContext glContext() const { return glContext_; }
/**
* @brief
*/
void swapBuffers();
/**
* @brief VSync
*/
void setVSync(bool enabled);
/** /**
* @brief * @brief
*/ */
std::unique_ptr<Buffer> createBuffer(BufferType type, BufferUsage usage, std::unique_ptr<Buffer> createBuffer(BufferType type, BufferUsage usage,
size_t size, const void* data = nullptr); size_t size, const void *data = nullptr);
/** /**
* @brief * @brief
*/ */
std::unique_ptr<VertexBuffer> createVertexBuffer(BufferUsage usage, size_t size, std::unique_ptr<VertexBuffer> createVertexBuffer(BufferUsage usage,
const void* data = nullptr); size_t size,
const void *data = nullptr);
/** /**
* @brief * @brief
*/ */
std::unique_ptr<IndexBuffer> createIndexBuffer(BufferUsage usage, size_t size, std::unique_ptr<IndexBuffer> createIndexBuffer(BufferUsage usage, size_t size,
const void* data = nullptr); const void *data = nullptr);
/** /**
* @brief Uniform * @brief Uniform
*/ */
std::unique_ptr<UniformBuffer> createUniformBuffer(BufferUsage usage, size_t size, std::unique_ptr<UniformBuffer>
const void* data = nullptr); createUniformBuffer(BufferUsage usage, size_t size,
const void *data = nullptr);
/** /**
* @brief VAO * @brief VAO
@ -108,23 +141,23 @@ public:
/** /**
* @brief * @brief
*/ */
void clear(bool color, bool depth, bool stencil, void clear(bool color, bool depth, bool stencil, float r = 0.0f,
float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 1.0f); float g = 0.0f, float b = 0.0f, float a = 1.0f);
/** /**
* @brief * @brief
*/ */
void setBlendState(const BlendState& state); void setBlendState(const BlendState &state);
/** /**
* @brief * @brief
*/ */
void setDepthState(const DepthState& state); void setDepthState(const DepthState &state);
/** /**
* @brief * @brief
*/ */
void setRasterState(const RasterState& state); void setRasterState(const RasterState &state);
/** /**
* @brief * @brief
@ -145,7 +178,7 @@ public:
* @brief * @brief
*/ */
void drawElements(PrimitiveType mode, int32 count, IndexType type, void drawElements(PrimitiveType mode, int32 count, IndexType type,
const void* indices = nullptr); const void *indices = nullptr);
/** /**
* @brief * @brief
@ -157,19 +190,22 @@ public:
* @brief * @brief
*/ */
void drawElementsInstanced(PrimitiveType mode, int32 count, IndexType type, void drawElementsInstanced(PrimitiveType mode, int32 count, IndexType type,
const void* indices, int32 instanceCount); const void *indices, int32 instanceCount);
private: private:
RenderDevice() = default; RenderDevice() = default;
~RenderDevice() = default; ~RenderDevice() = default;
RenderDevice(const RenderDevice&) = delete; RenderDevice(const RenderDevice &) = delete;
RenderDevice& operator=(const RenderDevice&) = delete; RenderDevice &operator=(const RenderDevice &) = delete;
bool initGL(const RenderDeviceConfig &config);
void queryCaps(); void queryCaps();
GLenum glPrimitiveType(PrimitiveType mode) const; GLenum glPrimitiveType(PrimitiveType mode) const;
GLenum glIndexType(IndexType type) const; GLenum glIndexType(IndexType type) const;
bool initialized_ = false; bool initialized_ = false;
SDL_Window *window_ = nullptr;
SDL_GLContext glContext_ = nullptr;
RenderCaps caps_; RenderCaps caps_;
BlendState currentBlend_; BlendState currentBlend_;

View File

@ -56,20 +56,12 @@ enum class CompareFunc : uint8 {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 剔除模式 // 剔除模式
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
enum class CullMode : uint8 { enum class CullMode : uint8 { None, Front, Back };
None,
Front,
Back
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 多边形模式 // 多边形模式
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
enum class PolygonMode : uint8 { enum class PolygonMode : uint8 { Fill, Line, Point };
Fill,
Line,
Point
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 混合因子 // 混合因子
@ -94,13 +86,7 @@ enum class BlendFactor : uint8 {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 混合操作 // 混合操作
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
enum class BlendOp : uint8 { enum class BlendOp : uint8 { Add, Subtract, ReverseSubtract, Min, Max };
Add,
Subtract,
ReverseSubtract,
Min,
Max
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 缓冲区类型 // 缓冲区类型
@ -116,11 +102,7 @@ enum class BufferType : uint8 {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 缓冲区使用方式 // 缓冲区使用方式
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
enum class BufferUsage : uint8 { enum class BufferUsage : uint8 { Static, Dynamic, Stream };
Static,
Dynamic,
Stream
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 纹理过滤模式 // 纹理过滤模式
@ -137,13 +119,7 @@ enum class FilterMode : uint8 {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 纹理环绕模式 // 纹理环绕模式
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
enum class WrapMode : uint8 { enum class WrapMode : uint8 { Repeat, Clamp, Mirror, MirrorClamp, Border };
Repeat,
Clamp,
Mirror,
MirrorClamp,
Border
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 图元类型 // 图元类型
@ -161,11 +137,7 @@ enum class PrimitiveType : uint8 {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 索引类型 // 索引类型
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
enum class IndexType : uint8 { enum class IndexType : uint8 { UInt8, UInt16, UInt32 };
UInt8,
UInt16,
UInt32
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 顶点属性语义 // 顶点属性语义
@ -225,13 +197,9 @@ struct BlendState {
BlendOp opAlpha = BlendOp::Add; BlendOp opAlpha = BlendOp::Add;
uint8 colorWriteMask = 0xF; uint8 colorWriteMask = 0xF;
static BlendState opaque() { static BlendState opaque() { return BlendState{false, BlendMode::None}; }
return BlendState{false, BlendMode::None};
}
static BlendState alpha() { static BlendState alpha() { return BlendState{true, BlendMode::Alpha}; }
return BlendState{true, BlendMode::Alpha};
}
static BlendState additive() { static BlendState additive() {
BlendState state; BlendState state;
@ -354,37 +322,11 @@ struct ClearFlags {
bool depth = true; bool depth = true;
bool stencil = false; bool stencil = false;
static ClearFlags all() { static ClearFlags all() { return ClearFlags{true, true, true}; }
return ClearFlags{true, true, true};
}
static ClearFlags colorOnly() { static ClearFlags colorOnly() { return ClearFlags{true, false, false}; }
return ClearFlags{true, false, false};
}
static ClearFlags depthOnly() { static ClearFlags depthOnly() { return ClearFlags{false, true, false}; }
return ClearFlags{false, true, false};
}
};
// ---------------------------------------------------------------------------
// 颜色 (RGBA)
// ---------------------------------------------------------------------------
struct Color {
float r = 0.0f;
float g = 0.0f;
float b = 0.0f;
float a = 1.0f;
Color() = default;
Color(float r, float g, float b, float a = 1.0f) : r(r), g(g), b(b), a(a) {}
static Color white() { return Color(1.0f, 1.0f, 1.0f, 1.0f); }
static Color black() { return Color(0.0f, 0.0f, 0.0f, 1.0f); }
static Color red() { return Color(1.0f, 0.0f, 0.0f, 1.0f); }
static Color green() { return Color(0.0f, 1.0f, 0.0f, 1.0f); }
static Color blue() { return Color(0.0f, 0.0f, 1.0f, 1.0f); }
static Color transparent() { return Color(0.0f, 0.0f, 0.0f, 0.0f); }
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,153 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/render/camera.h>
#include <extra2d/render/shape_renderer.h>
#include <extra2d/render/sprite_renderer.h>
#include <extra2d/render/texture.h>
#include <extra2d/render/render_context.h>
#include <glm/mat4x4.hpp>
#include <memory>
namespace extra2d {
/**
* @brief
*
* ShapeRenderer SpriteRenderer 2D API
*/
class Renderer {
public:
Renderer();
~Renderer();
/**
* @brief
*/
bool init();
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param clearColor
*/
void beginFrame(const Color& clearColor = Colors::Black);
/**
* @brief
*/
void endFrame();
/**
* @brief
*/
void setViewProjection(const glm::mat4& viewProjection);
/**
* @brief
*/
const glm::mat4& viewProjection() const { return viewProjection_; }
// =========================================================================
// 图形绘制 API
// =========================================================================
/**
* @brief 线
*/
void drawLine(const Vec2& start, const Vec2& end, const Color& color, float width = 1.0f);
/**
* @brief
*/
void drawRect(const Rect& rect, const Color& color, float width = 1.0f);
/**
* @brief
*/
void fillRect(const Rect& rect, const Color& color);
/**
* @brief
*/
void drawCircle(const Vec2& center, float radius, const Color& color,
int segments = 32, float width = 1.0f);
/**
* @brief
*/
void fillCircle(const Vec2& center, float radius, const Color& color, int segments = 32);
/**
* @brief
*/
void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
const Color& color, float width = 1.0f);
/**
* @brief
*/
void fillTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, const Color& color);
/**
* @brief
*/
void drawPolygon(const std::vector<Vec2>& points, const Color& color, float width = 1.0f);
/**
* @brief
*/
void fillPolygon(const std::vector<Vec2>& points, const Color& color);
// =========================================================================
// 精灵绘制 API
// =========================================================================
/**
* @brief
*/
void drawSprite(const Texture& texture, const Rect& destRect, const Rect& srcRect,
const Color& color = Colors::White, float rotation = 0.0f,
const Vec2& anchor = Vec2(0.5f, 0.5f));
/**
* @brief
*/
void drawSprite(const Texture& texture, const Vec2& position,
const Color& color = Colors::White);
/**
* @brief
*/
void drawSprite(const Texture& texture, const Vec2& position, const Vec2& scale,
const Color& color = Colors::White);
// =========================================================================
// 统计信息
// =========================================================================
/**
* @brief
*/
uint32 drawCalls() const;
/**
* @brief
*/
uint32 spriteCount() const;
private:
glm::vec4 toVec4(const Color& color) const;
std::unique_ptr<ShapeRenderer> shapeRenderer_;
std::unique_ptr<SpriteRenderer> spriteRenderer_;
glm::mat4 viewProjection_{1.0f};
bool inFrame_ = false;
};
} // namespace extra2d

View File

@ -0,0 +1,108 @@
#pragma once
#include <extra2d/core/types.h>
#include <extra2d/render/render_types.h>
#include <glad/glad.h>
#include <memory>
namespace extra2d {
/**
* @brief
*/
struct TextureConfig {
int width = 0;
int height = 0;
int channels = 4;
FilterMode filter = FilterMode::Linear;
WrapMode wrap = WrapMode::Clamp;
bool generateMips = false;
};
/**
* @brief GPU
*
* OpenGL
*/
class Texture {
public:
Texture() = default;
~Texture();
/**
* @brief
*/
static std::unique_ptr<Texture> create(const u8* data, const TextureConfig& config);
/**
* @brief
*/
static std::unique_ptr<Texture> createEmpty(int width, int height, int channels = 4);
/**
* @brief
*/
void update(const u8* data, int width, int height, int channels);
/**
* @brief
*/
void updateSubRegion(const u8* data, int x, int y, int width, int height);
/**
* @brief
*/
void bind(uint32 unit = 0) const;
/**
* @brief
*/
void unbind() const;
/**
* @brief OpenGL ID
*/
GLuint glHandle() const { return glHandle_; }
/**
* @brief
*/
int width() const { return width_; }
/**
* @brief
*/
int height() const { return height_; }
/**
* @brief
*/
int channels() const { return channels_; }
/**
* @brief
*/
bool isValid() const { return glHandle_ != 0; }
/**
* @brief
*/
void setFilter(FilterMode filter);
/**
* @brief
*/
void setWrap(WrapMode wrap);
private:
bool init(const u8* data, const TextureConfig& config);
GLenum glInternalFormat() const;
GLenum glFormat() const;
GLuint glHandle_ = 0;
int width_ = 0;
int height_ = 0;
int channels_ = 4;
};
} // namespace extra2d

View File

@ -1,21 +1,17 @@
#pragma once #pragma once
#include <algorithm>
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/event/event_dispatcher.h> #include <extra2d/event/event_dispatcher.h>
#include <extra2d/graphics/renderer.h> #include <extra2d/render/renderer.h>
#include <functional>
#include <string> #include <string>
#include <vector> #include <vector>
namespace extra2d { namespace extra2d {
// 前向声明
class Scene; class Scene;
class Renderer; class Renderer;
struct RenderCommand;
// ============================================================================ // ============================================================================
// 节点基类 - 场景图的基础 // 节点基类 - 场景图的基础
@ -25,30 +21,20 @@ public:
Node(); Node();
virtual ~Node(); virtual ~Node();
// ------------------------------------------------------------------------
// 层级管理 // 层级管理
// ------------------------------------------------------------------------ void addChild(Ref<Node> child);
void addChild(Ptr<Node> child); void addChildren(std::vector<Ref<Node>> &&children);
void removeChild(Ref<Node> child);
/**
* @brief
* @param children
*/
void addChildren(std::vector<Ptr<Node>> &&children);
void removeChild(Ptr<Node> child);
void removeChildByName(const std::string &name); void removeChildByName(const std::string &name);
void removeFromParent(); void removeFromParent();
void removeAllChildren(); void removeAllChildren();
Ptr<Node> getParent() const { return parent_.lock(); } Ref<Node> getParent() const { return parent_.lock(); }
const std::vector<Ptr<Node>> &getChildren() const { return children_; } const std::vector<Ref<Node>> &getChildren() const { return children_; }
Ptr<Node> getChildByName(const std::string &name) const; Ref<Node> getChildByName(const std::string &name) const;
Ptr<Node> getChildByTag(int tag) const; Ref<Node> getChildByTag(int tag) const;
// ------------------------------------------------------------------------
// 变换属性 // 变换属性
// ------------------------------------------------------------------------
void setPosition(const Vec2 &pos); void setPosition(const Vec2 &pos);
void setPosition(float x, float y); void setPosition(float x, float y);
Vec2 getPosition() const { return position_; } Vec2 getPosition() const { return position_; }
@ -75,66 +61,39 @@ public:
void setVisible(bool visible); void setVisible(bool visible);
bool isVisible() const { return visible_; } bool isVisible() const { return visible_; }
/** void setColor(const Color3B &color);
* @brief
* @param color RGB颜色
*/
void setColor(const Color3B& color);
Color3B getColor() const { return color_; } Color3B getColor() const { return color_; }
/**
* @brief X轴翻转
*/
void setFlipX(bool flipX); void setFlipX(bool flipX);
bool isFlipX() const { return flipX_; } bool isFlipX() const { return flipX_; }
/**
* @brief Y轴翻转
*/
void setFlipY(bool flipY); void setFlipY(bool flipY);
bool isFlipY() const { return flipY_; } bool isFlipY() const { return flipY_; }
void setZOrder(int zOrder); void setZOrder(int zOrder);
int getZOrder() const { return zOrder_; } int getZOrder() const { return zOrder_; }
// ------------------------------------------------------------------------
// 世界变换 // 世界变换
// ------------------------------------------------------------------------
Vec2 convertToWorldSpace(const Vec2 &localPos) const; Vec2 convertToWorldSpace(const Vec2 &localPos) const;
Vec2 convertToNodeSpace(const Vec2 &worldPos) const; Vec2 convertToNodeSpace(const Vec2 &worldPos) const;
glm::mat4 getLocalTransform() const; glm::mat4 getLocalTransform() const;
glm::mat4 getWorldTransform() const; glm::mat4 getWorldTransform() const;
/**
* @brief
*/
void markTransformDirty(); void markTransformDirty();
/**
* @brief
*
*/
void batchUpdateTransforms(); void batchUpdateTransforms();
/**
* @brief
*/
bool isTransformDirty() const { return transformDirty_; } bool isTransformDirty() const { return transformDirty_; }
bool isWorldTransformDirty() const { return worldTransformDirty_; } bool isWorldTransformDirty() const { return worldTransformDirty_; }
// ------------------------------------------------------------------------
// 名称和标签 // 名称和标签
// ------------------------------------------------------------------------
void setName(const std::string &name) { name_ = name; } void setName(const std::string &name) { name_ = name; }
const std::string &getName() const { return name_; } const std::string &getName() const { return name_; }
void setTag(int tag) { tag_ = tag; } void setTag(int tag) { tag_ = tag; }
int getTag() const { return tag_; } int getTag() const { return tag_; }
// ------------------------------------------------------------------------
// 生命周期回调 // 生命周期回调
// ------------------------------------------------------------------------
virtual void onEnter(); virtual void onEnter();
virtual void onExit(); virtual void onExit();
virtual void onUpdate(float dt); virtual void onUpdate(float dt);
@ -142,26 +101,17 @@ public:
virtual void onAttachToScene(Scene *scene); virtual void onAttachToScene(Scene *scene);
virtual void onDetachFromScene(); virtual void onDetachFromScene();
// ------------------------------------------------------------------------
// 边界框(用于空间索引) // 边界框(用于空间索引)
// ------------------------------------------------------------------------
virtual Rect getBoundingBox() const; virtual Rect getBoundingBox() const;
// 是否需要参与空间索引(默认 true
void setSpatialIndexed(bool indexed) { spatialIndexed_ = indexed; } void setSpatialIndexed(bool indexed) { spatialIndexed_ = indexed; }
bool isSpatialIndexed() const { return spatialIndexed_; } bool isSpatialIndexed() const { return spatialIndexed_; }
// 更新空间索引(手动调用,通常在边界框变化后)
void updateSpatialIndex(); void updateSpatialIndex();
// ------------------------------------------------------------------------
// 事件系统 // 事件系统
// ------------------------------------------------------------------------
EventDispatcher &getEventDispatcher() { return eventDispatcher_; } EventDispatcher &getEventDispatcher() { return eventDispatcher_; }
// ------------------------------------------------------------------------
// 内部方法 // 内部方法
// ------------------------------------------------------------------------
void update(float dt); void update(float dt);
void render(Renderer &renderer); void render(Renderer &renderer);
void sortChildren(); void sortChildren();
@ -169,18 +119,10 @@ public:
bool isRunning() const { return running_; } bool isRunning() const { return running_; }
Scene *getScene() const { return scene_; } Scene *getScene() const { return scene_; }
// 多线程渲染命令收集
virtual void collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder = 0);
protected: protected:
// 子类重写
virtual void onDraw(Renderer &renderer) {} virtual void onDraw(Renderer &renderer) {}
virtual void onUpdateNode(float dt) {} virtual void onUpdateNode(float dt) {}
virtual void generateRenderCommand(std::vector<RenderCommand> &commands,
int zOrder) {};
// 供子类访问的内部状态
Vec2 &getPositionRef() { return position_; } Vec2 &getPositionRef() { return position_; }
Vec2 &getScaleRef() { return scale_; } Vec2 &getScaleRef() { return scale_; }
Vec2 &getAnchorRef() { return anchor_; } Vec2 &getAnchorRef() { return anchor_; }
@ -188,65 +130,45 @@ protected:
float getOpacityRef() { return opacity_; } float getOpacityRef() { return opacity_; }
private: private:
// ========================================================================== mutable glm::mat4 localTransform_{1.0f};
// 成员变量按类型大小降序排列,减少内存对齐填充 mutable glm::mat4 worldTransform_{1.0f};
// 64位系统对齐std::string(32) > glm::mat4(64) > std::vector(24) >
// double(8) > float(4) > int(4) > bool(1)
// ==========================================================================
// 1. 大块内存64字节 std::string name_;
mutable glm::mat4 localTransform_; // 64 bytes std::vector<Ref<Node>> children_;
mutable glm::mat4 worldTransform_; // 64 bytes
// 2. 字符串和容器24-32字节 std::unordered_map<std::string, std::weak_ptr<Node>> nameIndex_;
std::string name_; // 32 bytes std::unordered_map<int, std::weak_ptr<Node>> tagIndex_;
std::vector<Ptr<Node>> children_; // 24 bytes
// 3. 子节点索引(加速查找) EventDispatcher eventDispatcher_;
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
// 4. 事件分发器 std::weak_ptr<Node> parent_;
EventDispatcher eventDispatcher_; // 大小取决于实现
// 5. 父节点引用 Vec2 position_ = Vec2::Zero();
WeakPtr<Node> parent_; // 16 bytes Vec2 scale_ = Vec2(1.0f, 1.0f);
Vec2 anchor_ = Vec2(0.5f, 0.5f);
Vec2 skew_ = Vec2::Zero();
// 7. 变换属性(按访问频率分组) Rect lastSpatialBounds_;
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;
Rect lastSpatialBounds_; // 16 bytes float opacity_ = 1.0f;
// 9. 浮点属性 Color3B color_ = Color3B(255, 255, 255);
float rotation_ = 0.0f; // 4 bytes
float opacity_ = 1.0f; // 4 bytes
// 10. 颜色属性 int zOrder_ = 0;
Color3B color_ = Color3B(255, 255, 255); // 3 bytes int tag_ = -1;
// 11. 整数属性 bool flipX_ = false;
int zOrder_ = 0; // 4 bytes bool flipY_ = false;
int tag_ = -1; // 4 bytes
// 12. 布尔属性 Scene *scene_ = nullptr;
bool flipX_ = false; // 1 byte
bool flipY_ = false; // 1 byte
// 11. 场景指针 mutable bool transformDirty_ = true;
Scene *scene_ = nullptr; // 8 bytes mutable bool worldTransformDirty_ = true;
bool childrenOrderDirty_ = false;
// 12. 布尔标志(打包在一起) bool visible_ = true;
mutable bool transformDirty_ = true; // 1 byte bool running_ = false;
mutable bool worldTransformDirty_ = true; // 1 byte bool spatialIndexed_ = true;
bool childrenOrderDirty_ = false; // 1 byte
bool visible_ = true; // 1 byte
bool running_ = false; // 1 byte
bool spatialIndexed_ = true; // 1 byte
// 填充 2 bytes 到 8 字节对齐
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -1,15 +1,13 @@
#pragma once #pragma once
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/graphics/camera.h> #include <extra2d/render/camera.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
#include <extra2d/spatial/spatial_manager.h> #include <extra2d/spatial/spatial_manager.h>
#include <vector> #include <vector>
namespace extra2d { namespace extra2d {
// 前向声明
struct RenderCommand;
class Renderer; class Renderer;
// ============================================================================ // ============================================================================
@ -20,74 +18,54 @@ public:
Scene(); Scene();
~Scene() override = default; ~Scene() override = default;
// ------------------------------------------------------------------------
// 场景属性 // 场景属性
// ------------------------------------------------------------------------ void setBackgroundColor(const Color& color) { backgroundColor_ = color; }
void setBackgroundColor(const Color &color) { backgroundColor_ = color; }
Color getBackgroundColor() const { return backgroundColor_; } Color getBackgroundColor() const { return backgroundColor_; }
// ------------------------------------------------------------------------
// 摄像机 // 摄像机
// ------------------------------------------------------------------------ void setCamera(Ref<Camera> camera);
void setCamera(Ptr<Camera> camera); Ref<Camera> getCamera() const { return camera_; }
Ptr<Camera> getCamera() const { return camera_; }
Camera *getActiveCamera() const { Camera* getActiveCamera() const {
return camera_ ? camera_.get() : defaultCamera_.get(); return camera_ ? camera_.get() : defaultCamera_.get();
} }
// ------------------------------------------------------------------------
// 视口和尺寸 // 视口和尺寸
// ------------------------------------------------------------------------
void setViewportSize(float width, float height); void setViewportSize(float width, float height);
void setViewportSize(const Size &size); void setViewportSize(const Size& size);
Size getViewportSize() const { return viewportSize_; } Size getViewportSize() const { return viewportSize_; }
float getWidth() const { return viewportSize_.width; } float getWidth() const { return viewportSize_.width; }
float getHeight() const { return viewportSize_.height; } float getHeight() const { return viewportSize_.height; }
// ------------------------------------------------------------------------
// 场景状态 // 场景状态
// ------------------------------------------------------------------------
bool isPaused() const { return paused_; } bool isPaused() const { return paused_; }
void pause() { paused_ = true; } void pause() { paused_ = true; }
void resume() { paused_ = false; } void resume() { paused_ = false; }
// ------------------------------------------------------------------------
// 渲染和更新 // 渲染和更新
// ------------------------------------------------------------------------ void renderScene(Renderer& renderer);
void renderScene(Renderer &renderer); virtual void renderContent(Renderer& renderer);
virtual void renderContent(Renderer &renderer);
void updateScene(float dt); void updateScene(float dt);
void collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder = 0) override;
// ------------------------------------------------------------------------
// 空间索引系统 // 空间索引系统
// ------------------------------------------------------------------------ SpatialManager& getSpatialManager() { return spatialManager_; }
SpatialManager &getSpatialManager() { return spatialManager_; } const SpatialManager& getSpatialManager() const { return spatialManager_; }
const SpatialManager &getSpatialManager() const { return spatialManager_; }
// 启用/禁用空间索引
void setSpatialIndexingEnabled(bool enabled) { void setSpatialIndexingEnabled(bool enabled) {
spatialIndexingEnabled_ = enabled; spatialIndexingEnabled_ = enabled;
} }
bool isSpatialIndexingEnabled() const { return spatialIndexingEnabled_; } bool isSpatialIndexingEnabled() const { return spatialIndexingEnabled_; }
// 节点空间索引管理(内部使用) void updateNodeInSpatialIndex(Node* node, const Rect& oldBounds, const Rect& newBounds);
void updateNodeInSpatialIndex(Node *node, const Rect &oldBounds, void removeNodeFromSpatialIndex(Node* node);
const Rect &newBounds);
void removeNodeFromSpatialIndex(Node *node);
// 碰撞检测查询 std::vector<Node*> queryNodesInArea(const Rect& area) const;
std::vector<Node *> queryNodesInArea(const Rect &area) const; std::vector<Node*> queryNodesAtPoint(const Vec2& point) const;
std::vector<Node *> queryNodesAtPoint(const Vec2 &point) const; std::vector<std::pair<Node*, Node*>> queryCollisions() const;
std::vector<std::pair<Node *, Node *>> queryCollisions() const;
// ------------------------------------------------------------------------
// 静态创建方法 // 静态创建方法
// ------------------------------------------------------------------------ static Ref<Scene> create();
static Ptr<Scene> create();
protected: protected:
void onEnter() override; void onEnter() override;
@ -99,12 +77,11 @@ private:
Color backgroundColor_ = Colors::Black; Color backgroundColor_ = Colors::Black;
Size viewportSize_ = Size::Zero(); Size viewportSize_ = Size::Zero();
Ptr<Camera> camera_; Ref<Camera> camera_;
Ptr<Camera> defaultCamera_; Ref<Camera> defaultCamera_;
bool paused_ = false; bool paused_ = false;
// 空间索引系统
SpatialManager spatialManager_; SpatialManager spatialManager_;
bool spatialIndexingEnabled_ = true; bool spatialIndexingEnabled_ = true;
}; };

View File

@ -1,98 +1,62 @@
#pragma once #pragma once
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/render/renderer.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <functional>
#include <stack> #include <stack>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector>
namespace extra2d { namespace extra2d {
// 前向声明
struct RenderCommand;
class Renderer;
// ============================================================================ // ============================================================================
// 场景管理器 - 管理场景的生命周期和切换 // 场景管理器 - 管理场景的生命周期和切换
// ============================================================================ // ============================================================================
class SceneManager { class SceneManager {
public: public:
// ------------------------------------------------------------------------
// 单例访问
// ------------------------------------------------------------------------
static SceneManager &getInstance(); static SceneManager &getInstance();
// ------------------------------------------------------------------------
// 场景栈操作 // 场景栈操作
// ------------------------------------------------------------------------ void runWithScene(Ref<Scene> scene);
void replaceScene(Ref<Scene> scene);
// 运行第一个场景 void pushScene(Ref<Scene> scene);
void runWithScene(Ptr<Scene> scene);
// 替换当前场景
void replaceScene(Ptr<Scene> scene);
// 压入新场景(当前场景暂停)
void pushScene(Ptr<Scene> scene);
// 弹出当前场景(恢复上一个场景)
void popScene(); void popScene();
// 弹出到根场景
void popToRootScene(); void popToRootScene();
// 弹出到指定场景
void popToScene(const std::string &name); void popToScene(const std::string &name);
// ------------------------------------------------------------------------
// 获取场景 // 获取场景
// ------------------------------------------------------------------------ Ref<Scene> getCurrentScene() const;
Ptr<Scene> getCurrentScene() const; Ref<Scene> getPreviousScene() const;
Ptr<Scene> getPreviousScene() const; Ref<Scene> getRootScene() const;
Ptr<Scene> getRootScene() const; Ref<Scene> getSceneByName(const std::string &name) const;
// 通过名称获取场景
Ptr<Scene> getSceneByName(const std::string &name) const;
// ------------------------------------------------------------------------
// 查询 // 查询
// ------------------------------------------------------------------------
size_t getSceneCount() const { return sceneStack_.size(); } size_t getSceneCount() const { return sceneStack_.size(); }
bool isEmpty() const { return sceneStack_.empty(); } bool isEmpty() const { return sceneStack_.empty(); }
bool hasScene(const std::string &name) const; bool hasScene(const std::string &name) const;
// ------------------------------------------------------------------------
// 更新和渲染 // 更新和渲染
// ------------------------------------------------------------------------
void update(float dt); void update(float dt);
void render(Renderer &renderer); void render(Renderer &renderer);
void collectRenderCommands(std::vector<RenderCommand> &commands);
// ------------------------------------------------------------------------
// 过渡控制 // 过渡控制
// ------------------------------------------------------------------------
bool isTransitioning() const { return false; } bool isTransitioning() const { return false; }
// ------------------------------------------------------------------------
// 清理 // 清理
// ------------------------------------------------------------------------
void end(); void end();
void purgeCachedScenes(); void purgeCachedScenes();
public: // 场景切换(供 Application 使用)
void enterScene(Ref<Scene> scene);
private:
SceneManager() = default; SceneManager() = default;
~SceneManager() = default; ~SceneManager() = default;
SceneManager(const SceneManager &) = delete; SceneManager(const SceneManager &) = delete;
SceneManager &operator=(const SceneManager &) = delete; SceneManager &operator=(const SceneManager &) = delete;
// 场景切换(供 Application 使用) std::stack<Ref<Scene>> sceneStack_;
void enterScene(Ptr<Scene> scene); std::unordered_map<std::string, Ref<Scene>> namedScenes_;
private:
std::stack<Ptr<Scene>> sceneStack_;
std::unordered_map<std::string, Ptr<Scene>> namedScenes_;
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -20,52 +20,35 @@ public:
ShapeNode(); ShapeNode();
~ShapeNode() override = default; ~ShapeNode() override = default;
// ------------------------------------------------------------------------
// 静态创建方法 // 静态创建方法
// ------------------------------------------------------------------------ static Ref<ShapeNode> create();
static Ptr<ShapeNode> create();
// 点 static Ref<ShapeNode> createPoint(const Vec2& pos, const Color& color);
static Ptr<ShapeNode> createPoint(const Vec2 &pos, const Color &color); static Ref<ShapeNode> createLine(const Vec2& start, const Vec2& end,
const Color& color, float width = 1.0f);
// 线 static Ref<ShapeNode> createRect(const Rect& rect, 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); float width = 1.0f);
static Ptr<ShapeNode> createFilledRect(const Rect &rect, const Color &color); static Ref<ShapeNode> createFilledRect(const Rect& rect, const Color& color);
static Ref<ShapeNode> createCircle(const Vec2& center, float radius,
// 圆形 const Color& color, int segments = 32,
static Ptr<ShapeNode> createCircle(const Vec2 &center, float radius,
const Color &color, int segments = 32,
float width = 1.0f); float width = 1.0f);
static Ptr<ShapeNode> createFilledCircle(const Vec2 &center, float radius, static Ref<ShapeNode> createFilledCircle(const Vec2& center, float radius,
const Color &color, const Color& color, int segments = 32);
int segments = 32); static Ref<ShapeNode> createTriangle(const Vec2& p1, const Vec2& p2,
const Vec2& p3, const Color& color,
// 三角形
static Ptr<ShapeNode> createTriangle(const Vec2 &p1, const Vec2 &p2,
const Vec2 &p3, const Color &color,
float width = 1.0f); float width = 1.0f);
static Ptr<ShapeNode> createFilledTriangle(const Vec2 &p1, const Vec2 &p2, static Ref<ShapeNode> createFilledTriangle(const Vec2& p1, const Vec2& p2,
const Vec2 &p3, const Vec2& p3, const Color& color);
const Color &color); static Ref<ShapeNode> createPolygon(const std::vector<Vec2>& points,
const Color& color, float width = 1.0f);
static Ref<ShapeNode> createFilledPolygon(const std::vector<Vec2>& points,
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; } void setShapeType(ShapeType type) { shapeType_ = type; }
ShapeType getShapeType() const { return shapeType_; } ShapeType getShapeType() const { return shapeType_; }
void setColor(const Color &color) { color_ = color; } void setColor(const Color& color) { color_ = color; }
Color getColor() const { return color_; } Color getColor() const { return color_; }
void setFilled(bool filled) { filled_ = filled; } void setFilled(bool filled) { filled_ = filled; }
@ -77,20 +60,15 @@ public:
void setSegments(int segments) { segments_ = segments; } void setSegments(int segments) { segments_ = segments; }
int getSegments() const { return 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 setPoints(const std::vector<Vec2> &points);
const std::vector<Vec2> &getPoints() const { return points_; }
void addPoint(const Vec2 &point);
void clearPoints(); void clearPoints();
Rect getBoundingBox() const override; Rect getBoundingBox() const override;
protected: protected:
void onDraw(Renderer &renderer) override; void onDraw(Renderer& renderer) override;
void generateRenderCommand(std::vector<RenderCommand> &commands,
int zOrder) override;
private: private:
ShapeType shapeType_ = ShapeType::Rect; ShapeType shapeType_ = ShapeType::Rect;

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <extra2d/graphics/texture.h> #include <extra2d/render/texture.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
namespace extra2d { namespace extra2d {
@ -11,19 +11,19 @@ namespace extra2d {
class Sprite : public Node { class Sprite : public Node {
public: public:
Sprite(); Sprite();
explicit Sprite(Ptr<Texture> texture); explicit Sprite(Ref<Texture> texture);
~Sprite() override = default; ~Sprite() override = default;
// 纹理 // 纹理
void setTexture(Ptr<Texture> texture); void setTexture(Ref<Texture> texture);
Ptr<Texture> getTexture() const { return texture_; } Ref<Texture> getTexture() const { return texture_; }
// 纹理矩形 (用于图集) // 纹理矩形 (用于图集)
void setTextureRect(const Rect &rect); void setTextureRect(const Rect& rect);
Rect getTextureRect() const { return textureRect_; } Rect getTextureRect() const { return textureRect_; }
// 颜色混合 // 颜色混合
void setColor(const Color &color); void setColor(const Color& color);
Color getColor() const { return color_; } Color getColor() const { return color_; }
// 翻转 // 翻转
@ -33,19 +33,17 @@ public:
bool isFlipY() const { return flipY_; } bool isFlipY() const { return flipY_; }
// 静态创建方法 // 静态创建方法
static Ptr<Sprite> create(); static Ref<Sprite> create();
static Ptr<Sprite> create(Ptr<Texture> texture); static Ref<Sprite> create(Ref<Texture> texture);
static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect); static Ref<Sprite> create(Ref<Texture> texture, const Rect& rect);
Rect getBoundingBox() const override; Rect getBoundingBox() const override;
protected: protected:
void onDraw(Renderer &renderer) override; void onDraw(Renderer& renderer) override;
void generateRenderCommand(std::vector<RenderCommand> &commands,
int zOrder) override;
private: private:
Ptr<Texture> texture_; Ref<Texture> texture_;
Rect textureRect_; Rect textureRect_;
Color color_ = Colors::White; Color color_ = Colors::White;
bool flipX_ = false; bool flipX_ = false;

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cstdint>
#include <extra2d/core/service_interface.h> #include <extra2d/core/service_interface.h>
#include <extra2d/core/service_locator.h> #include <extra2d/core/service_locator.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
@ -10,7 +9,7 @@
namespace extra2d { namespace extra2d {
/** /**
* @brief * @brief
*/ */
struct LogColor { struct LogColor {
u8 r, g, b; u8 r, g, b;
@ -18,36 +17,22 @@ struct LogColor {
constexpr LogColor() : r(255), g(255), b(255) {} constexpr LogColor() : r(255), g(255), b(255) {}
constexpr LogColor(u8 r, u8 g, u8 b) : r(r), g(g), b(b) {} constexpr LogColor(u8 r, u8 g, u8 b) : r(r), g(g), b(b) {}
static constexpr LogColor White() { return LogColor(255, 255, 255); } static constexpr LogColor White() { return {255, 255, 255}; }
static constexpr LogColor Gray() { return LogColor(128, 128, 128); } static constexpr LogColor Gray() { return {128, 128, 128}; }
static constexpr LogColor Red() { return LogColor(255, 85, 85); } static constexpr LogColor Red() { return {255, 85, 85}; }
static constexpr LogColor Green() { return LogColor(85, 255, 85); } static constexpr LogColor Green() { return {85, 255, 85}; }
static constexpr LogColor Yellow() { return LogColor(255, 255, 85); } static constexpr LogColor Yellow() { return {255, 255, 85}; }
static constexpr LogColor Blue() { return LogColor(85, 85, 255); } static constexpr LogColor Blue() { return {85, 85, 255}; }
static constexpr LogColor Magenta() { return LogColor(255, 85, 255); } static constexpr LogColor Magenta() { return {255, 85, 255}; }
static constexpr LogColor Cyan() { return LogColor(85, 255, 255); } static constexpr LogColor Cyan() { return {85, 255, 255}; }
static constexpr LogColor Orange() { return LogColor(255, 165, 0); } static constexpr LogColor SkyLight() { return {125, 211, 252}; }
static constexpr LogColor IndigoLight() { return {165, 180, 252}; }
static constexpr LogColor Slate() { return LogColor(100, 116, 139); }
static constexpr LogColor SlateLight() { return LogColor(148, 163, 184); }
static constexpr LogColor Sky() { return LogColor(14, 165, 233); }
static constexpr LogColor SkyLight() { return LogColor(125, 211, 252); }
static constexpr LogColor Emerald() { return LogColor(16, 185, 129); }
static constexpr LogColor EmeraldLight() { return LogColor(110, 231, 183); }
static constexpr LogColor Amber() { return LogColor(245, 158, 11); }
static constexpr LogColor AmberLight() { return LogColor(252, 211, 77); }
static constexpr LogColor Rose() { return LogColor(244, 63, 94); }
static constexpr LogColor RoseLight() { return LogColor(253, 164, 175); }
static constexpr LogColor Violet() { return LogColor(139, 92, 246); }
static constexpr LogColor VioletLight() { return LogColor(196, 181, 253); }
static constexpr LogColor Indigo() { return LogColor(99, 102, 241); }
static constexpr LogColor IndigoLight() { return LogColor(165, 180, 252); }
}; };
/** /**
* @brief * @brief
*/ */
enum class LogLevel { enum class LogLevel : u8 {
Trace = 0, Trace = 0,
Debug = 1, Debug = 1,
Info = 2, Info = 2,
@ -65,102 +50,36 @@ class ILogger : public IService {
public: public:
virtual ~ILogger() = default; virtual ~ILogger() = default;
/**
* @brief
*/
virtual void level(LogLevel lvl) = 0; virtual void level(LogLevel lvl) = 0;
/**
* @brief
*/
virtual LogLevel level() const = 0; virtual LogLevel level() const = 0;
/**
* @brief
*/
virtual bool enabled(LogLevel lvl) const = 0; virtual bool enabled(LogLevel lvl) const = 0;
/**
* @brief
*/
virtual void log(LogLevel lvl, const char *fmt, ...) = 0; virtual void log(LogLevel lvl, const char *fmt, ...) = 0;
/**
* @brief
*/
virtual void log(LogLevel lvl, const std::string &msg) = 0; virtual void log(LogLevel lvl, const std::string &msg) = 0;
/**
* @brief Trace级别日志
*/
virtual void trace(const char *fmt, ...) = 0; virtual void trace(const char *fmt, ...) = 0;
/**
* @brief Debug级别日志
*/
virtual void debug(const char *fmt, ...) = 0; virtual void debug(const char *fmt, ...) = 0;
/**
* @brief Info级别日志
*/
virtual void info(const char *fmt, ...) = 0; virtual void info(const char *fmt, ...) = 0;
/**
* @brief Registry级别日志/
*/
virtual void registry(const char *fmt, ...) = 0; virtual void registry(const char *fmt, ...) = 0;
/**
* @brief Warn级别日志
*/
virtual void warn(const char *fmt, ...) = 0; virtual void warn(const char *fmt, ...) = 0;
/**
* @brief Error级别日志
*/
virtual void error(const char *fmt, ...) = 0; virtual void error(const char *fmt, ...) = 0;
/**
* @brief Fatal级别日志
*/
virtual void fatal(const char *fmt, ...) = 0; virtual void fatal(const char *fmt, ...) = 0;
/**
* @brief
* @param lvl
* @param c
*/
virtual void levelColor(LogLevel lvl, const LogColor &c) = 0; virtual void levelColor(LogLevel lvl, const LogColor &c) = 0;
/**
* @brief
* @param lvl
* @return
*/
virtual LogColor levelColor(LogLevel lvl) const = 0; virtual LogColor levelColor(LogLevel lvl) const = 0;
/**
* @brief /
* @param on
*/
virtual void colors(bool on) = 0; virtual void colors(bool on) = 0;
/**
* @brief
*/
virtual bool colors() const = 0; virtual bool colors() const = 0;
ServiceInfo info() const override { ServiceInfo info() const override {
ServiceInfo i; ServiceInfo i;
i.name = "Logger"; i.name = "Logger";
i.priority = ServicePriority::Core; i.priority = ServicePriority::Core;
i.enabled = true;
return i; return i;
} }
}; };
/** /**
* @brief * @brief
*/ */
class ConsoleLogger : public ILogger { class ConsoleLogger : public ILogger {
public: public:
@ -195,38 +114,32 @@ private:
const char *levelString(LogLevel lvl); const char *levelString(LogLevel lvl);
std::string ansiColor(LogLevel lvl); std::string ansiColor(LogLevel lvl);
LogLevel level_; LogLevel level_ = LogLevel::Info;
bool colors_; bool colors_ = true;
LogColor levelColors_[7]; LogColor levelColors_[7];
class Impl; class Impl;
Unique<Impl> impl_; Unique<Impl> impl_;
// 服务注册元数据
E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger); E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger);
}; };
} // namespace extra2d } // namespace extra2d
// 格式化辅助函数 - 将参数转换为字符串 // 格式化辅助
namespace extra2d { namespace extra2d {
namespace detail { namespace detail {
template <typename T> std::string to_string(T &&value) { template <typename T> std::string to_string(T &&value) {
using Decayed = std::decay_t<T>; using D = std::decay_t<T>;
if constexpr (std::is_same_v<Decayed, std::string>) { if constexpr (std::is_same_v<D, std::string>)
return value; return value;
} else if constexpr (std::is_same_v<Decayed, const char *>) { else if constexpr (std::is_same_v<D, const char *>)
return value ? value : "(null)"; return value ? value : "(null)";
} else if constexpr (std::is_arithmetic_v<Decayed>) { else if constexpr (std::is_same_v<D, bool>)
if constexpr (std::is_same_v<Decayed, bool>) {
return value ? "true" : "false"; return value ? "true" : "false";
} else if constexpr (std::is_floating_point_v<Decayed>) { else if constexpr (std::is_arithmetic_v<D>)
return std::to_string(value); return std::to_string(value);
} else { else
return std::to_string(value);
}
} else {
return "<?>"; return "<?>";
}
} }
inline void format_impl(std::string &result, const char *fmt) { result += fmt; } inline void format_impl(std::string &result, const char *fmt) { result += fmt; }
@ -234,42 +147,33 @@ inline void format_impl(std::string &result, const char *fmt) { result += fmt; }
template <typename T, typename... Args> template <typename T, typename... Args>
void format_impl(std::string &result, const char *fmt, T &&value, void format_impl(std::string &result, const char *fmt, T &&value,
Args &&...args) { Args &&...args) {
const char *p = fmt; while (*fmt) {
while (*p) { if (*fmt == '{' && *(fmt + 1) == '}') {
if (*p == '{' && *(p + 1) == '}') {
result += to_string(std::forward<T>(value)); result += to_string(std::forward<T>(value));
format_impl(result, p + 2, std::forward<Args>(args)...); return format_impl(result, fmt + 2, std::forward<Args>(args)...);
return;
} }
result += *p++; result += *fmt++;
} }
result += " "; result += " " + to_string(std::forward<T>(value));
result += to_string(std::forward<T>(value)); format_impl(result, fmt, std::forward<Args>(args)...);
format_impl(result, p, std::forward<Args>(args)...);
} }
} // namespace detail } // namespace detail
template <typename... Args> template <typename... Args>
std::string format_str(const char *fmt, Args &&...args) { std::string format_str(const char *fmt, Args &&...args) {
if constexpr (sizeof...(args) == 0) {
return std::string(fmt);
} else {
std::string result; std::string result;
detail::format_impl(result, fmt, std::forward<Args>(args)...); detail::format_impl(result, fmt, std::forward<Args>(args)...);
return result; return result;
}
} }
} // namespace extra2d } // namespace extra2d
// 便捷宏 - 自动获取日志服务 // 日志宏
#define E2D_LOG(lvl, ...) \ #define E2D_LOG(lvl, ...) \
do { \ do { \
if (auto logService = ::extra2d::ServiceLocator::instance() \ if (auto log = ::extra2d::ServiceLocator::instance() \
.tryGet<::extra2d::ILogger>()) { \ .tryGet<::extra2d::ILogger>()) \
if (logService->enabled(lvl)) { \ if (log->enabled(lvl)) \
logService->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \ log->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \
} \
} \
} while (0) } while (0)
#define E2D_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__) #define E2D_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
@ -279,32 +183,3 @@ std::string format_str(const char *fmt, Args &&...args) {
#define E2D_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__) #define E2D_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__) #define E2D_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__) #define E2D_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
// 带颜色参数的日志宏
#define E2D_LOG_COLOR(lvl, c, ...) \
do { \
if (auto logService = ::extra2d::ServiceLocator::instance() \
.tryGet<::extra2d::ILogger>()) { \
if (logService->enabled(lvl)) { \
auto prevColor = logService->levelColor(lvl); \
logService->levelColor(lvl, c); \
logService->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \
logService->levelColor(lvl, prevColor); \
} \
} \
} while (0)
#define E2D_TRACE_COLOR(c, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Trace, c, __VA_ARGS__)
#define E2D_DEBUG_COLOR(c, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Debug, c, __VA_ARGS__)
#define E2D_INFO_COLOR(c, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Info, c, __VA_ARGS__)
#define E2D_REGISTRY_COLOR(c, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Registry, c, __VA_ARGS__)
#define E2D_WARN_COLOR(c, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Warn, c, __VA_ARGS__)
#define E2D_ERROR_COLOR(c, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Error, c, __VA_ARGS__)
#define E2D_FATAL_COLOR(c, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Fatal, c, __VA_ARGS__)

View File

@ -1,270 +0,0 @@
#pragma once
#include <cstdarg>
#include <cstdio>
#include <extra2d/core/types.h>
#include <sstream>
#include <string>
#include <type_traits>
// SDL2 日志头文件
#include <SDL.h>
namespace extra2d {
// ============================================================================
// 日志级别枚举 - 映射到 SDL_LogPriority
// ============================================================================
enum class LogLevel {
Trace = SDL_LOG_PRIORITY_VERBOSE, // SDL 详细日志
Debug = SDL_LOG_PRIORITY_DEBUG, // SDL 调试日志
Info = SDL_LOG_PRIORITY_INFO, // SDL 信息日志
Warn = SDL_LOG_PRIORITY_WARN, // SDL 警告日志
Error = SDL_LOG_PRIORITY_ERROR, // SDL 错误日志
Fatal = SDL_LOG_PRIORITY_CRITICAL, // SDL 严重日志
Off = SDL_LOG_PRIORITY_CRITICAL + 1 // 关闭日志 (使用 Critical+1 作为关闭标记)
};
// ============================================================================
// 简单的 fmt-style {} 格式化器
// ============================================================================
namespace detail {
// 将单个参数转为字符串
template <typename T> inline std::string to_string_arg(const T &value) {
if constexpr (std::is_same_v<T, std::string>) {
return value;
} else if constexpr (std::is_same_v<T, const char *> ||
std::is_same_v<T, char *>) {
return value ? std::string(value) : std::string("(null)");
} else if constexpr (std::is_same_v<T, bool>) {
return value ? "true" : "false";
} else if constexpr (std::is_arithmetic_v<T>) {
// 对浮点数使用特殊格式
if constexpr (std::is_floating_point_v<T>) {
char buf[64];
snprintf(buf, sizeof(buf), "%.2f", static_cast<double>(value));
return buf;
} else {
return std::to_string(value);
}
} else {
std::ostringstream oss;
oss << value;
return oss.str();
}
}
// 格式化基础情况:没有更多参数
inline std::string format_impl(const char *fmt) {
std::string result;
while (*fmt) {
if (*fmt == '{' && *(fmt + 1) == '}') {
result += "{}"; // 无参数可替换,保留原样
fmt += 2;
} else {
result += *fmt;
++fmt;
}
}
return result;
}
// 格式化递归:替换第一个 {} 并递归处理剩余
template <typename T, typename... Args>
inline std::string format_impl(const char *fmt, const T &first,
const Args &...rest) {
std::string result;
while (*fmt) {
if (*fmt == '{') {
// 检查 {:#x} 等格式说明符
if (*(fmt + 1) == '}') {
result += to_string_arg(first);
fmt += 2;
result += format_impl(fmt, rest...);
return result;
} else if (*(fmt + 1) == ':') {
// 跳过格式说明符直到 }
const char *end = fmt + 2;
while (*end && *end != '}')
++end;
if (*end == '}') {
// 检查是否是十六进制格式
std::string spec(fmt + 2, end);
if (spec.find('x') != std::string::npos ||
spec.find('X') != std::string::npos) {
if constexpr (std::is_integral_v<T>) {
char buf[32];
snprintf(buf, sizeof(buf), "0x%x",
static_cast<unsigned int>(first));
result += buf;
} else {
result += to_string_arg(first);
}
} else if (spec.find('f') != std::string::npos ||
spec.find('.') != std::string::npos) {
if constexpr (std::is_arithmetic_v<T>) {
// 解析精度
int precision = 2;
auto dot = spec.find('.');
if (dot != std::string::npos) {
precision = 0;
for (size_t i = dot + 1;
i < spec.size() && spec[i] >= '0' && spec[i] <= '9'; ++i) {
precision = precision * 10 + (spec[i] - '0');
}
}
char fmtbuf[16];
snprintf(fmtbuf, sizeof(fmtbuf), "%%.%df", precision);
char buf[64];
snprintf(buf, sizeof(buf), fmtbuf, static_cast<double>(first));
result += buf;
} else {
result += to_string_arg(first);
}
} else {
result += to_string_arg(first);
}
fmt = end + 1;
result += format_impl(fmt, rest...);
return result;
}
}
}
result += *fmt;
++fmt;
}
return result;
}
} // namespace detail
// 顶层格式化函数
template <typename... Args>
inline std::string e2d_format(const char *fmt, const Args &...args) {
return detail::format_impl(fmt, args...);
}
// 无参数版本
inline std::string e2d_format(const char *fmt) { return std::string(fmt); }
// ============================================================================
// Logger 类 - 使用 SDL2 日志系统
// ============================================================================
class Logger {
public:
/**
* @brief
*/
static void init();
/**
* @brief
*/
static void shutdown();
/**
* @brief
* @param level
*/
static void setLevel(LogLevel level);
/**
* @brief
* @param enable
*/
static void setConsoleOutput(bool enable);
/**
* @brief
* @param filename
*/
static void setFileOutput(const std::string &filename);
/**
* @brief
* @return
*/
static LogLevel getLevel() { return level_; }
/**
* @brief
* @param level
* @param fmt
* @param args
*/
template <typename... Args>
static void log(LogLevel level, const char *fmt, const Args &...args) {
if (static_cast<int>(level) < static_cast<int>(level_))
return;
std::string msg = e2d_format(fmt, args...);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level), "[%s] %s",
getLevelString(level), msg.c_str());
}
/**
* @brief
* @param level
* @param msg
*/
static void log(LogLevel level, const char *msg) {
if (static_cast<int>(level) < static_cast<int>(level_))
return;
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level), "[%s] %s",
getLevelString(level), msg);
}
private:
static LogLevel level_; // 当前日志级别
static bool initialized_; // 是否已初始化
static bool consoleOutput_; // 是否输出到控制台
static bool fileOutput_; // 是否输出到文件
static std::string logFile_; // 日志文件路径
/**
* @brief
* @param level
* @return
*/
static const char *getLevelString(LogLevel level);
};
// ============================================================================
// 日志宏
// ============================================================================
#ifdef E2D_DEBUG
#define E2D_LOG_TRACE(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__)
#define E2D_LOG_DEBUG(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__)
#else
#define E2D_LOG_TRACE(...)
#define E2D_LOG_DEBUG(...)
#endif
#define E2D_LOG_INFO(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__)
#define E2D_LOG_WARN(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_LOG_ERROR(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_LOG_FATAL(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__)
// 简化的日志宏
#define E2D_INFO(...) E2D_LOG_INFO(__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__)
#ifdef E2D_DEBUG
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
#else
#define E2D_DEBUG_LOG(...)
#define E2D_TRACE(...)
#endif
} // namespace extra2d

View File

@ -1,31 +1,18 @@
#pragma once #pragma once
<<<<<<< HEAD
#include <extra2d/core/math_types.h>
#include <extra2d/core/string.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
=======
#include <extra2d/core/types.h>
#include <extra2d/core/math_types.h>
#include <string> #include <string>
#include <functional>
>>>>>>> 9bf328b1dca01df84a07724394abc9a238739869
#include <SDL.h> #include <SDL.h>
namespace extra2d { namespace extra2d {
class Input;
struct WindowConfig { struct WindowConfig {
std::string title = "Extra2D Application"; std::string title = "Extra2D Application";
int width = 1280; int width = 1280;
int height = 720; int height = 720;
bool fullscreen = true; bool fullscreen = true;
bool resizable = false; bool resizable = false;
bool vsync = true;
int msaaSamples = 0;
bool centerWindow = true; bool centerWindow = true;
}; };
@ -34,42 +21,29 @@ public:
Window(); Window();
~Window(); ~Window();
bool create(const WindowConfig &config); bool create(const WindowConfig& config);
void destroy(); void destroy();
void pollEvents(); void pollEvents();
void swapBuffers();
bool shouldClose() const; bool shouldClose() const;
void setShouldClose(bool close); void setShouldClose(bool close);
void setTitle(const std::string &title); void setTitle(const std::string& title);
void setSize(int width, int height); void setSize(int width, int height);
void setFullscreen(bool fullscreen); void setFullscreen(bool fullscreen);
void setVSync(bool enabled);
int getWidth() const { return width_; } int getWidth() const { return width_; }
int getHeight() const { return height_; } int getHeight() const { return height_; }
bool isFullscreen() const { return fullscreen_; } bool isFullscreen() const { return fullscreen_; }
bool isVSync() const { return vsync_; }
SDL_Window *getSDLWindow() const { return sdlWindow_; } SDL_Window* sdlWindow() const { return sdlWindow_; }
SDL_GLContext getGLContext() const { return glContext_; }
Input *getInput() const { return input_.get(); }
private: private:
SDL_Window *sdlWindow_; SDL_Window* sdlWindow_ = nullptr;
SDL_GLContext glContext_; int width_ = 1280;
int height_ = 720;
int width_; bool fullscreen_ = true;
int height_; bool shouldClose_ = false;
bool vsync_;
bool shouldClose_;
bool fullscreen_;
UniquePtr<Input> input_;
bool initSDL(const WindowConfig &config);
void deinitSDL();
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -1,68 +1,49 @@
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
<<<<<<< HEAD #include <extra2d/render/render_device.h>
#include <extra2d/audio/audio_engine.h> #include <extra2d/services/logger_service.h>
#include <extra2d/event/event_dispatcher.h>
#include <extra2d/event/event_queue.h>
#include <extra2d/graphics/renderer.h>
#include <extra2d/utils/logger.h>
#include <extra2d/window/window.h> #include <extra2d/window/window.h>
#include <chrono> #include <SDL.h>
#include <thread>
=======
#include <extra2d/core/registry.h>
#include <extra2d/platform/glfw/glfw_window.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/services/event_service.h>
#include <extra2d/services/logger_service.h>
#include <extra2d/services/timer_service.h>
>>>>>>> 9bf328b1dca01df84a07724394abc9a238739869
namespace extra2d { namespace extra2d {
static f64 getTimeSeconds() { Application &Application::instance() {
#ifdef __SWITCH__ static Application app;
struct timespec ts; return app;
clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<f64>(ts.tv_sec) +
static_cast<f64>(ts.tv_nsec) / 1000000000.0;
#else
using namespace std::chrono;
auto now = steady_clock::now();
auto duration = now.time_since_epoch();
return duration_cast<std::chrono::duration<f64>>(duration).count();
#endif
} }
Application &Application::get() { Application::~Application() { shutdown(); }
static Application instance;
return instance;
}
Application::Application() { Registry::instance().setApp(this); }
Application::~Application() {
if (initialized_) {
shutdown();
}
}
bool Application::init() { bool Application::init() {
if (initialized_) { if (initialized_)
return true; return true;
}
// 初始化所有模块(拓扑排序) E2D_INFO("Application initializing: {}", name);
// 服务通过 E2D_AUTO_REGISTER_SERVICE 宏自动注册
if (!Registry::instance().init()) { ServiceLocator::instance().init();
window_ = new Window();
if (!window_->create({.title = name, .width = 1280, .height = 720})) {
E2D_ERROR("Failed to create window");
delete window_;
window_ = nullptr;
return false; return false;
} }
// 初始化所有服务 auto &device = RenderDevice::instance();
ServiceLocator::instance().init(); if (!device.init(window_->sdlWindow())) {
E2D_ERROR("Failed to initialize render device");
window_->destroy();
delete window_;
window_ = nullptr;
return false;
}
initialized_ = true; initialized_ = true;
running_ = true; running_ = true;
lastFrameTime_ = SDL_GetPerformanceCounter();
E2D_INFO("Application initialized successfully");
return true; return true;
} }
@ -70,10 +51,17 @@ void Application::shutdown() {
if (!initialized_) if (!initialized_)
return; return;
E2D_INFO("Application shutting down");
RenderDevice::instance().shutdown();
if (window_) {
window_->destroy();
delete window_;
window_ = nullptr;
}
ServiceLocator::instance().shutdown(); ServiceLocator::instance().shutdown();
ServiceLocator::instance().clear();
Registry::instance().shutdown();
Registry::instance().clear();
initialized_ = false; initialized_ = false;
running_ = false; running_ = false;
@ -83,95 +71,33 @@ void Application::run() {
if (!initialized_) if (!initialized_)
return; return;
auto *winMod = get<WindowModule>(); while (running_ && !window_->shouldClose()) {
if (!winMod || !winMod->win())
return;
lastFrameTime_ = getTimeSeconds();
while (running_ && !winMod->win()->shouldClose()) {
mainLoop(); mainLoop();
} }
} }
<<<<<<< HEAD
void Application::quit() { running_ = false; } void Application::quit() { running_ = false; }
=======
void Application::quit() {
shouldQuit_ = true;
running_ = false;
}
>>>>>>> 9bf328b1dca01df84a07724394abc9a238739869
void Application::pause() {
if (!paused_) {
paused_ = true;
ServiceLocator::instance().pause();
}
}
void Application::resume() {
if (paused_) {
paused_ = false;
ServiceLocator::instance().resume();
lastFrameTime_ = getTimeSeconds();
}
}
void Application::mainLoop() { void Application::mainLoop() {
f64 currentTime = getTimeSeconds(); u64 currentTime = SDL_GetPerformanceCounter();
dt_ = static_cast<f32>(currentTime - lastFrameTime_); u64 frequency = SDL_GetPerformanceFrequency();
dt_ = static_cast<f32>(currentTime - lastFrameTime_) /
static_cast<f32>(frequency);
lastFrameTime_ = currentTime; lastFrameTime_ = currentTime;
totalTime_ += dt_; totalTime_ += dt_;
frameCount_++; frameCount_++;
fpsTimer_ += dt_; fpsTimer_ += dt_;
if (fpsTimer_ >= 1.0f) { if (fpsTimer_ >= 1.0f) {
fps_ = frameCount_; fps_ = frameCount_;
frameCount_ = 0; frameCount_ = 0;
fpsTimer_ -= 1.0f; fpsTimer_ -= 1.0f;
} }
auto *winMod = get<WindowModule>(); window_->pollEvents();
if (winMod && winMod->win()) { ServiceLocator::instance().update(dt_);
winMod->win()->poll(); RenderDevice::instance().swapBuffers();
}
auto eventService = ServiceLocator::instance().get<IEventService>();
if (eventService) {
eventService->process();
}
if (!paused_) {
update();
}
render();
}
void Application::update() { ServiceLocator::instance().update(dt_); }
void Application::render() {
auto *winMod = get<WindowModule>();
if (!winMod || !winMod->win())
return;
<<<<<<< HEAD
renderer_->beginFrame(Colors::Black);
// 渲染内容可以在这里添加
renderer_->endFrame();
window_->swapBuffers();
=======
winMod->win()->swap();
>>>>>>> 9bf328b1dca01df84a07724394abc9a238739869
}
GLFWWindow *Application::window() {
auto *winMod = get<WindowModule>();
return winMod ? winMod->win() : nullptr;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,6 +1,6 @@
#include "extra2d/audio/audio_engine.h" #include "extra2d/audio/audio_engine.h"
#include "extra2d/audio/sound.h" #include "extra2d/audio/sound.h"
#include "extra2d/utils/logger.h" #include <extra2d/services/logger_service.h>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h> #include <SDL2/SDL_mixer.h>

View File

@ -1,6 +1,6 @@
#include <SDL2/SDL_mixer.h> #include <SDL2/SDL_mixer.h>
#include <extra2d/audio/sound.h> #include <extra2d/audio/sound.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {

View File

@ -1,285 +0,0 @@
#include <extra2d/graphics/opengl/gl_font_atlas.h>
#include <extra2d/utils/logger.h>
#include <simdutf/simdutf.h>
#include <fstream>
#define STB_TRUETYPE_IMPLEMENTATION
#include <stb/stb_truetype.h>
#define STB_RECT_PACK_IMPLEMENTATION
#include <stb/stb_rect_pack.h>
#include <algorithm>
namespace extra2d {
// ============================================================================
// 构造函数 - 初始化字体图集
// ============================================================================
GLFontAtlas::GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF)
: fontSize_(fontSize), useSDF_(useSDF), currentY_(0), scale_(0.0f),
ascent_(0.0f), descent_(0.0f), lineGap_(0.0f) {
// 加载字体文件
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
E2D_LOG_ERROR("Failed to load font: {}", filepath);
return;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
fontData_.resize(size);
if (!file.read(reinterpret_cast<char *>(fontData_.data()), size)) {
E2D_LOG_ERROR("Failed to read font file: {}", filepath);
return;
}
// 初始化 stb_truetype
if (!stbtt_InitFont(&fontInfo_, fontData_.data(),
stbtt_GetFontOffsetForIndex(fontData_.data(), 0))) {
E2D_LOG_ERROR("Failed to init font: {}", 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_;
createAtlas();
}
// ============================================================================
// 析构函数
// ============================================================================
GLFontAtlas::~GLFontAtlas() = default;
// ============================================================================
// 获取字形 - 如果字形不存在则缓存它
// ============================================================================
const Glyph *GLFontAtlas::getGlyph(char32_t codepoint) const {
auto it = glyphs_.find(codepoint);
if (it == glyphs_.end()) {
cacheGlyph(codepoint);
it = glyphs_.find(codepoint);
}
return (it != glyphs_.end()) ? &it->second : nullptr;
}
// ============================================================================
// 测量文本尺寸
// ============================================================================
Vec2 GLFontAtlas::measureText(const std::string &text) {
float width = 0.0f;
float height = getAscent() - getDescent();
float currentWidth = 0.0f;
std::u32string utf32_text;
utf32_text.resize(simdutf::utf32_length_from_utf8(text.data(), text.size()));
simdutf::convert_utf8_to_utf32(text.data(), text.size(), utf32_text.data());
for (char32_t codepoint : utf32_text) {
if (codepoint == '\n') {
width = std::max(width, currentWidth);
currentWidth = 0.0f;
height += getLineHeight();
continue;
}
const Glyph *glyph = getGlyph(codepoint);
if (glyph) {
currentWidth += glyph->advance;
}
}
width = std::max(width, currentWidth);
return Vec2(width, height);
}
// ============================================================================
// 创建图集纹理 - 初始化空白纹理和矩形打包上下文
// ============================================================================
void GLFontAtlas::createAtlas() {
// 统一使用 4 通道格式
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);
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) const {
int advance = 0;
stbtt_GetCodepointHMetrics(&fontInfo_, static_cast<int>(codepoint), &advance,
nullptr);
float advancePx = advance * scale_;
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);
Glyph glyph{};
glyph.advance = advancePx;
glyphs_[codepoint] = glyph;
return;
}
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("Font atlas is full, cannot cache codepoint: {}",
static_cast<int>(codepoint));
stbtt_FreeSDF(sdf, nullptr);
return;
}
int atlasX = rect.x + PADDING;
int atlasY = rect.y + PADDING;
Glyph glyph;
glyph.width = static_cast<float>(w);
glyph.height = static_cast<float>(h);
glyph.bearingX = static_cast<float>(xoff);
glyph.bearingY = static_cast<float>(yoff);
glyph.advance = advancePx;
// stb_rect_pack 使用左上角为原点OpenGL纹理使用左下角为原点
// 需要翻转V坐标
float v0 = static_cast<float>(atlasY) / ATLAS_HEIGHT;
float v1 = static_cast<float>(atlasY + h) / ATLAS_HEIGHT;
glyph.u0 = static_cast<float>(atlasX) / ATLAS_WIDTH;
glyph.v0 = 1.0f - v1; // 翻转V坐标
glyph.u1 = static_cast<float>(atlasX + w) / ATLAS_WIDTH;
glyph.v1 = 1.0f - v0; // 翻转V坐标
glyphs_[codepoint] = glyph;
// 将 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 通道
}
// 直接设置像素对齐为 4无需查询当前状态
glBindTexture(GL_TEXTURE_2D, texture_->getTextureID());
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
// OpenGL纹理坐标原点在左下角需要将Y坐标翻转
glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h,
GL_RGBA, GL_UNSIGNED_BYTE, glyphRgbaCache_.data());
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;
int yoff = y0;
if (w <= 0 || h <= 0) {
Glyph glyph{};
glyph.advance = advancePx;
glyphs_[codepoint] = glyph;
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("Font atlas is full, cannot cache codepoint: {}",
static_cast<int>(codepoint));
return;
}
int atlasX = rect.x + PADDING;
int atlasY = rect.y + PADDING;
// 创建字形信息
Glyph glyph;
glyph.width = static_cast<float>(w);
glyph.height = static_cast<float>(h);
glyph.bearingX = static_cast<float>(xoff);
glyph.bearingY = static_cast<float>(yoff);
glyph.advance = advancePx;
// 计算纹理坐标(相对于图集)
// stb_rect_pack 使用左上角为原点OpenGL纹理使用左下角为原点
// 需要翻转V坐标
float v0 = static_cast<float>(atlasY) / ATLAS_HEIGHT;
float v1 = static_cast<float>(atlasY + h) / ATLAS_HEIGHT;
glyph.u0 = static_cast<float>(atlasX) / ATLAS_WIDTH;
glyph.v0 = 1.0f - v1; // 翻转V坐标
glyph.u1 = static_cast<float>(atlasX + w) / ATLAS_WIDTH;
glyph.v1 = 1.0f - v0; // 翻转V坐标
// 存储字形
glyphs_[codepoint] = glyph;
// 将单通道字形数据转换为 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
}
// 更新纹理 - 将字形数据上传到图集的指定位置
// 直接设置像素对齐为 4无需查询当前状态
glBindTexture(GL_TEXTURE_2D, texture_->getTextureID());
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
// OpenGL纹理坐标原点在左下角需要将Y坐标翻转
glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h,
GL_RGBA, GL_UNSIGNED_BYTE, glyphRgbaCache_.data());
}
} // namespace extra2d

View File

@ -1,629 +0,0 @@
#include <SDL.h>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <simdutf/simdutf.h>
#include <extra2d/graphics/gpu_context.h>
#include <extra2d/graphics/opengl/gl_font_atlas.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/vram_manager.h>
#include <extra2d/platform/window.h>
#include <extra2d/utils/logger.h>
#include <vector>
namespace extra2d {
// 形状渲染着色器 - 支持顶点颜色批处理 (GLES 3.2)
static const char *SHAPE_VERTEX_SHADER = R"(
#version 300 es
precision highp float;
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec4 aColor;
uniform mat4 uViewProjection;
out vec4 vColor;
void main() {
gl_Position = uViewProjection * vec4(aPosition, 0.0, 1.0);
vColor = aColor;
}
)";
static const char *SHAPE_FRAGMENT_SHADER = R"(
#version 300 es
precision highp float;
in vec4 vColor;
out vec4 fragColor;
void main() {
fragColor = vColor;
}
)";
// VBO 初始大小(用于 VRAM 跟踪)
static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float);
// ============================================================================
// BlendMode 查找表 - 编译期构建,运行时 O(1) 查找
// ============================================================================
struct BlendState {
bool enable;
GLenum srcFactor;
GLenum dstFactor;
};
static constexpr BlendState BLEND_STATES[] = {
{false, 0, 0}, // BlendMode::None
{true, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, // BlendMode::Alpha
{true, GL_SRC_ALPHA, GL_ONE}, // BlendMode::Additive
{true, GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA} // BlendMode::Multiply
};
static constexpr size_t BLEND_STATE_COUNT =
sizeof(BLEND_STATES) / sizeof(BLEND_STATES[0]);
GLRenderer::GLRenderer()
: window_(nullptr), shapeVao_(0), shapeVbo_(0), lineVao_(0), lineVbo_(0),
vsync_(true), shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES),
lineVertexCount_(0), currentLineWidth_(1.0f) {
resetStats();
for (auto &v : shapeVertexCache_) {
v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
}
for (auto &v : lineVertexCache_) {
v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
}
}
GLRenderer::~GLRenderer() { shutdown(); }
bool GLRenderer::init(Window *window) {
window_ = window;
// Switch: GL 上下文已通过 SDL2 + EGL 初始化,无需 glewInit()
// 初始化精灵批渲染器
if (!spriteBatch_.init()) {
E2D_LOG_ERROR("Failed to initialize sprite batch");
return false;
}
// 初始化形状渲染
initShapeRendering();
// 设置 OpenGL 状态
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 标记 GPU 上下文为有效
GPUContext::getInstance().markValid();
E2D_LOG_INFO("OpenGL Renderer initialized");
E2D_LOG_INFO("OpenGL Version: {}",
reinterpret_cast<const char *>(glGetString(GL_VERSION)));
return true;
}
void GLRenderer::shutdown() {
// 标记 GPU 上下文为无效
// 这会在销毁 OpenGL 上下文之前通知所有 GPU 资源
GPUContext::getInstance().markInvalid();
spriteBatch_.shutdown();
if (lineVbo_ != 0) {
glDeleteBuffers(1, &lineVbo_);
VRAMManager::getInstance().freeBuffer(MAX_LINE_VERTICES *
sizeof(ShapeVertex));
lineVbo_ = 0;
}
if (lineVao_ != 0) {
glDeleteVertexArrays(1, &lineVao_);
lineVao_ = 0;
}
if (shapeVbo_ != 0) {
glDeleteBuffers(1, &shapeVbo_);
VRAMManager::getInstance().freeBuffer(MAX_SHAPE_VERTICES *
sizeof(ShapeVertex));
shapeVbo_ = 0;
}
if (shapeVao_ != 0) {
glDeleteVertexArrays(1, &shapeVao_);
shapeVao_ = 0;
}
}
void GLRenderer::beginFrame(const Color &clearColor) {
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
glClear(GL_COLOR_BUFFER_BIT);
resetStats();
}
void GLRenderer::endFrame() {
// 刷新所有待处理的形状批次
flushShapeBatch();
// 刷新所有待处理的线条批次
flushLineBatch();
}
void GLRenderer::setViewport(int x, int y, int width, int height) {
glViewport(x, y, width, height);
}
void GLRenderer::setVSync(bool enabled) {
vsync_ = enabled;
// 使用 SDL2 设置交换间隔
SDL_GL_SetSwapInterval(enabled ? 1 : 0);
}
void GLRenderer::setBlendMode(BlendMode mode) {
// 状态缓存检查,避免冗余 GL 调用
if (cachedBlendMode_ == mode) {
return;
}
cachedBlendMode_ = mode;
// 使用查找表替代 switch
size_t index = static_cast<size_t>(mode);
if (index >= BLEND_STATE_COUNT) {
index = 0;
}
const BlendState &state = BLEND_STATES[index];
if (state.enable) {
if (!blendEnabled_) {
glEnable(GL_BLEND);
blendEnabled_ = true;
}
glBlendFunc(state.srcFactor, state.dstFactor);
} else {
if (blendEnabled_) {
glDisable(GL_BLEND);
blendEnabled_ = false;
}
}
}
void GLRenderer::setViewProjection(const glm::mat4 &matrix) {
viewProjection_ = matrix;
}
void GLRenderer::pushTransform(const glm::mat4 &transform) {
if (transformStack_.empty()) {
transformStack_.push_back(transform);
} else {
transformStack_.push_back(transformStack_.back() * transform);
}
}
void GLRenderer::popTransform() {
if (!transformStack_.empty()) {
transformStack_.pop_back();
}
}
glm::mat4 GLRenderer::getCurrentTransform() const {
if (transformStack_.empty()) {
return glm::mat4(1.0f);
}
return transformStack_.back();
}
Ptr<Texture> GLRenderer::createTexture(int width, int height,
const uint8_t *pixels, int channels) {
return makePtr<GLTexture>(width, height, pixels, channels);
}
Ptr<Texture> GLRenderer::loadTexture(const std::string &filepath) {
return makePtr<GLTexture>(filepath);
}
void GLRenderer::beginSpriteBatch() { spriteBatch_.begin(viewProjection_); }
void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect,
const Rect &srcRect, const Color &tint,
float rotation, const Vec2 &anchor) {
GLSpriteBatch::SpriteData data;
data.position = glm::vec2(destRect.origin.x, destRect.origin.y);
data.size = glm::vec2(destRect.size.width, destRect.size.height);
Texture *tex = const_cast<Texture *>(&texture);
float texW = static_cast<float>(tex->getWidth());
float texH = static_cast<float>(tex->getHeight());
// 纹理坐标计算
float u1 = srcRect.origin.x / texW;
float u2 = (srcRect.origin.x + srcRect.size.width) / texW;
float v1 = srcRect.origin.y / texH;
float v2 = (srcRect.origin.y + srcRect.size.height) / texH;
data.texCoordMin = glm::vec2(glm::min(u1, u2), glm::min(v1, v2));
data.texCoordMax = glm::vec2(glm::max(u1, u2), glm::max(v1, v2));
data.color = glm::vec4(tint.r, tint.g, tint.b, tint.a);
data.rotation = rotation * 3.14159f / 180.0f;
data.anchor = glm::vec2(anchor.x, anchor.y);
data.isSDF = false;
spriteBatch_.draw(texture, data);
}
void GLRenderer::drawSprite(const Texture &texture, const Vec2 &position,
const Color &tint) {
Rect destRect(position.x, position.y, static_cast<float>(texture.getWidth()),
static_cast<float>(texture.getHeight()));
Rect srcRect(0, 0, static_cast<float>(texture.getWidth()),
static_cast<float>(texture.getHeight()));
drawSprite(texture, destRect, srcRect, tint, 0.0f, Vec2(0, 0));
}
void GLRenderer::endSpriteBatch() {
spriteBatch_.end();
stats_.drawCalls += spriteBatch_.getDrawCallCount();
}
void GLRenderer::drawLine(const Vec2 &start, const Vec2 &end,
const Color &color, float width) {
// 如果线宽改变,需要先刷新线条批次
if (width != currentLineWidth_) {
flushLineBatch();
currentLineWidth_ = width;
}
// 添加两个顶点到线条缓冲区
addLineVertex(start.x, start.y, color);
addLineVertex(end.x, end.y, color);
}
void GLRenderer::drawRect(const Rect &rect, const Color &color, float width) {
// 如果线宽改变,需要先刷新线条批次
if (width != currentLineWidth_) {
flushLineBatch();
currentLineWidth_ = width;
}
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;
// 4条线段 = 8个顶点
// 上边
addLineVertex(x1, y1, color);
addLineVertex(x2, y1, color);
// 右边
addLineVertex(x2, y1, color);
addLineVertex(x2, y2, color);
// 下边
addLineVertex(x2, y2, color);
addLineVertex(x1, y2, color);
// 左边
addLineVertex(x1, y2, color);
addLineVertex(x1, y1, color);
}
void GLRenderer::fillRect(const Rect &rect, const Color &color) {
// 提交当前批次(如果模式不同)
submitShapeBatch(GL_TRIANGLES);
// 添加两个三角形组成矩形6个顶点
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;
// 三角形1: (x1,y1), (x2,y1), (x2,y2)
addShapeVertex(x1, y1, color);
addShapeVertex(x2, y1, color);
addShapeVertex(x2, y2, color);
// 三角形2: (x1,y1), (x2,y2), (x1,y2)
addShapeVertex(x1, y1, color);
addShapeVertex(x2, y2, color);
addShapeVertex(x1, y2, color);
}
void GLRenderer::drawCircle(const Vec2 &center, float radius,
const Color &color, int segments, float width) {
// 限制段数不超过缓存大小
if (segments > static_cast<int>(MAX_CIRCLE_SEGMENTS)) {
segments = static_cast<int>(MAX_CIRCLE_SEGMENTS);
}
// 如果线宽改变,需要先刷新线条批次
if (width != currentLineWidth_) {
flushLineBatch();
currentLineWidth_ = width;
}
// 使用线条批处理绘制圆形
for (int i = 0; i < segments; ++i) {
float angle1 =
2.0f * 3.14159f * static_cast<float>(i) / static_cast<float>(segments);
float angle2 = 2.0f * 3.14159f * static_cast<float>(i + 1) /
static_cast<float>(segments);
addLineVertex(center.x + radius * cosf(angle1),
center.y + radius * sinf(angle1), color);
addLineVertex(center.x + radius * cosf(angle2),
center.y + radius * sinf(angle2), color);
}
}
void GLRenderer::fillCircle(const Vec2 &center, float radius,
const Color &color, int segments) {
// 限制段数不超过缓存大小
if (segments > static_cast<int>(MAX_CIRCLE_SEGMENTS)) {
segments = static_cast<int>(MAX_CIRCLE_SEGMENTS);
}
// 提交当前批次(如果模式不同)
submitShapeBatch(GL_TRIANGLES);
// 使用三角形扇形填充圆
// 中心点 + 边缘点
for (int i = 0; i < segments; ++i) {
float angle1 =
2.0f * 3.14159f * static_cast<float>(i) / static_cast<float>(segments);
float angle2 = 2.0f * 3.14159f * static_cast<float>(i + 1) /
static_cast<float>(segments);
// 每个三角形:中心 -> 边缘点1 -> 边缘点2
addShapeVertex(center.x, center.y, color);
addShapeVertex(center.x + radius * cosf(angle1),
center.y + radius * sinf(angle1), color);
addShapeVertex(center.x + radius * cosf(angle2),
center.y + radius * sinf(angle2), color);
}
}
void GLRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Color &color, float width) {
drawLine(p1, p2, color, width);
drawLine(p2, p3, color, width);
drawLine(p3, p1, color, width);
}
void GLRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Color &color) {
submitShapeBatch(GL_TRIANGLES);
addShapeVertex(p1.x, p1.y, color);
addShapeVertex(p2.x, p2.y, color);
addShapeVertex(p3.x, p3.y, color);
}
void GLRenderer::drawPolygon(const std::vector<Vec2> &points,
const Color &color, float width) {
if (points.size() < 2)
return;
// 如果线宽改变,需要先刷新线条批次
if (width != currentLineWidth_) {
flushLineBatch();
currentLineWidth_ = width;
}
// 绘制所有边
for (size_t i = 0; i < points.size(); ++i) {
const Vec2 &p1 = points[i];
const Vec2 &p2 = points[(i + 1) % points.size()];
addLineVertex(p1.x, p1.y, color);
addLineVertex(p2.x, p2.y, color);
}
}
void GLRenderer::fillPolygon(const std::vector<Vec2> &points,
const Color &color) {
if (points.size() < 3)
return;
submitShapeBatch(GL_TRIANGLES);
// 使用三角形扇形填充
// 从第一个点开始,每两个相邻点组成一个三角形
for (size_t i = 1; i < points.size() - 1; ++i) {
addShapeVertex(points[0].x, points[0].y, color);
addShapeVertex(points[i].x, points[i].y, color);
addShapeVertex(points[i + 1].x, points[i + 1].y, color);
}
}
Ptr<FontAtlas> GLRenderer::createFontAtlas(const std::string &filepath,
int fontSize, bool useSDF) {
return makePtr<GLFontAtlas>(filepath, fontSize, useSDF);
}
void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
const Vec2 &position, const Color &color) {
drawText(font, text, position.x, position.y, color);
}
void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
float x, float y, const Color &color) {
float cursorX = x;
float cursorY = y;
float baselineY = cursorY + font.getAscent();
// 收集所有字符数据用于批处理
std::vector<GLSpriteBatch::SpriteData> sprites;
sprites.reserve(text.size()); // 预分配空间
std::u32string utf32_text;
utf32_text.resize(simdutf::utf32_length_from_utf8(text.data(), text.size()));
simdutf::convert_utf8_to_utf32(text.data(), text.size(), utf32_text.data());
for (char32_t codepoint : utf32_text) {
if (codepoint == '\n') {
cursorX = x;
cursorY += font.getLineHeight();
baselineY = cursorY + font.getAscent();
continue;
}
const Glyph *glyph = font.getGlyph(codepoint);
if (glyph) {
float penX = cursorX;
cursorX += glyph->advance;
if (glyph->width <= 0.0f || glyph->height <= 0.0f) {
continue;
}
float xPos = penX + glyph->bearingX;
float yPos = baselineY + glyph->bearingY;
GLSpriteBatch::SpriteData data;
data.position = glm::vec2(xPos, yPos);
data.size = glm::vec2(glyph->width, glyph->height);
data.texCoordMin = glm::vec2(glyph->u0, glyph->v0);
data.texCoordMax = glm::vec2(glyph->u1, glyph->v1);
data.color = glm::vec4(color.r, color.g, color.b, color.a);
data.rotation = 0.0f;
data.anchor = glm::vec2(0.0f, 0.0f);
data.isSDF = font.isSDF();
sprites.push_back(data);
}
}
// 使用批处理绘制所有字符
if (!sprites.empty()) {
spriteBatch_.drawBatch(*font.getTexture(), sprites);
}
}
void GLRenderer::resetStats() { stats_ = Stats{}; }
void GLRenderer::initShapeRendering() {
// 编译形状着色器
shapeShader_.compileFromSource(SHAPE_VERTEX_SHADER, SHAPE_FRAGMENT_SHADER);
// 创建形状 VAO 和 VBO
glGenVertexArrays(1, &shapeVao_);
glGenBuffers(1, &shapeVbo_);
glBindVertexArray(shapeVao_);
glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_);
glBufferData(GL_ARRAY_BUFFER, MAX_SHAPE_VERTICES * sizeof(ShapeVertex),
nullptr, GL_DYNAMIC_DRAW);
// 位置属性 (location = 0)
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(offsetof(ShapeVertex, x)));
// 颜色属性 (location = 1)
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(offsetof(ShapeVertex, r)));
glBindVertexArray(0);
// 创建线条专用 VAO 和 VBO
glGenVertexArrays(1, &lineVao_);
glGenBuffers(1, &lineVbo_);
glBindVertexArray(lineVao_);
glBindBuffer(GL_ARRAY_BUFFER, lineVbo_);
glBufferData(GL_ARRAY_BUFFER, MAX_LINE_VERTICES * sizeof(ShapeVertex),
nullptr, GL_DYNAMIC_DRAW);
// 位置属性 (location = 0)
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(offsetof(ShapeVertex, x)));
// 颜色属性 (location = 1)
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(offsetof(ShapeVertex, r)));
glBindVertexArray(0);
// VRAM 跟踪
VRAMManager::getInstance().allocBuffer(MAX_SHAPE_VERTICES *
sizeof(ShapeVertex));
VRAMManager::getInstance().allocBuffer(MAX_LINE_VERTICES *
sizeof(ShapeVertex));
}
void GLRenderer::addShapeVertex(float x, float y, const Color &color) {
if (shapeVertexCount_ >= MAX_SHAPE_VERTICES) {
flushShapeBatch();
}
ShapeVertex &v = shapeVertexCache_[shapeVertexCount_++];
v.x = x;
v.y = y;
v.r = color.r;
v.g = color.g;
v.b = color.b;
v.a = color.a;
}
void GLRenderer::addLineVertex(float x, float y, const Color &color) {
if (lineVertexCount_ >= MAX_LINE_VERTICES) {
flushLineBatch();
}
ShapeVertex &v = lineVertexCache_[lineVertexCount_++];
v.x = x;
v.y = y;
v.r = color.r;
v.g = color.g;
v.b = color.b;
v.a = color.a;
}
void GLRenderer::submitShapeBatch(GLenum mode) {
if (shapeVertexCount_ == 0)
return;
// 如果模式改变,先刷新
if (currentShapeMode_ != mode && shapeVertexCount_ > 0) {
flushShapeBatch();
}
currentShapeMode_ = mode;
}
void GLRenderer::flushShapeBatch() {
if (shapeVertexCount_ == 0)
return;
shapeShader_.bind();
shapeShader_.setMat4("uViewProjection", viewProjection_);
glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_);
glBufferSubData(GL_ARRAY_BUFFER, 0, shapeVertexCount_ * sizeof(ShapeVertex),
shapeVertexCache_.data());
glBindVertexArray(shapeVao_);
glDrawArrays(currentShapeMode_, 0, static_cast<GLsizei>(shapeVertexCount_));
stats_.drawCalls++;
stats_.triangleCount += static_cast<uint32_t>(shapeVertexCount_ / 3);
shapeVertexCount_ = 0;
}
void GLRenderer::flushLineBatch() {
if (lineVertexCount_ == 0)
return;
// 先刷新形状批次
flushShapeBatch();
glLineWidth(currentLineWidth_);
shapeShader_.bind();
shapeShader_.setMat4("uViewProjection", viewProjection_);
glBindBuffer(GL_ARRAY_BUFFER, lineVbo_);
glBufferSubData(GL_ARRAY_BUFFER, 0, lineVertexCount_ * sizeof(ShapeVertex),
lineVertexCache_.data());
glBindVertexArray(lineVao_);
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(lineVertexCount_));
stats_.drawCalls++;
lineVertexCount_ = 0;
}
} // namespace extra2d

View File

@ -1,19 +1,20 @@
#include <extra2d/core/color.h>
#include <extra2d/render/render_context.h> #include <extra2d/render/render_context.h>
#include <extra2d/render/render_stats.h> #include <extra2d/render/render_stats.h>
namespace extra2d { namespace extra2d {
RenderContext& RenderContext::instance() { RenderContext &RenderContext::instance() {
static RenderContext instance; static RenderContext instance;
return instance; return instance;
} }
bool RenderContext::init() { bool RenderContext::init(SDL_Window *window, const RenderDeviceConfig &config) {
if (initialized_) { if (initialized_) {
return true; return true;
} }
if (!device_.init()) { if (!device_.init(window, config)) {
return false; return false;
} }
@ -21,7 +22,7 @@ bool RenderContext::init() {
viewport_ = {0, 0, 800, 600}; viewport_ = {0, 0, 800, 600};
scissor_ = {0, 0, 0, 0, false}; scissor_ = {0, 0, 0, 0, false};
clearColor_ = Color::black(); clearColor_ = Colors::Black;
currentBlend_ = BlendState::opaque(); currentBlend_ = BlendState::opaque();
currentDepth_ = DepthState{}; currentDepth_ = DepthState{};
@ -37,8 +38,10 @@ void RenderContext::shutdown() {
} }
queue_.clear(); queue_.clear();
while (!viewportStack_.empty()) viewportStack_.pop(); while (!viewportStack_.empty())
while (!scissorStack_.empty()) scissorStack_.pop(); viewportStack_.pop();
while (!scissorStack_.empty())
scissorStack_.pop();
device_.shutdown(); device_.shutdown();
initialized_ = false; initialized_ = false;
@ -50,29 +53,25 @@ void RenderContext::beginFrame() {
E2D_RENDER_STATS().reset(); E2D_RENDER_STATS().reset();
} }
void RenderContext::endFrame() { void RenderContext::endFrame() { flush(); }
flush();
}
void RenderContext::submit(const RenderCommand& cmd) { void RenderContext::submit(const RenderCommand &cmd) { queue_.addCommand(cmd); }
queue_.addCommand(cmd);
}
void RenderContext::submit(RenderCommand&& cmd) { void RenderContext::submit(RenderCommand &&cmd) {
queue_.addCommand(std::move(cmd)); queue_.addCommand(std::move(cmd));
} }
void RenderContext::flush() { void RenderContext::flush() {
queue_.sort(); queue_.sort();
for (const auto& cmd : queue_.commands()) { for (const auto &cmd : queue_.commands()) {
executeCommand(cmd); executeCommand(cmd);
} }
queue_.clear(); queue_.clear();
} }
void RenderContext::executeCommand(const RenderCommand& cmd) { void RenderContext::executeCommand(const RenderCommand &cmd) {
if (cmd.type == RenderCommandType::None) { if (cmd.type == RenderCommandType::None) {
return; return;
} }
@ -117,17 +116,18 @@ void RenderContext::executeCommand(const RenderCommand& cmd) {
break; break;
case RenderCommandType::DrawElements: case RenderCommandType::DrawElements:
device_.drawElements(cmd.primitive, cmd.indexCount, cmd.indexType, cmd.indices); device_.drawElements(cmd.primitive, cmd.indexCount, cmd.indexType,
cmd.indices);
break; break;
case RenderCommandType::DrawArraysInstanced: case RenderCommandType::DrawArraysInstanced:
device_.drawArraysInstanced(cmd.primitive, cmd.firstVertex, device_.drawArraysInstanced(cmd.primitive, cmd.firstVertex, cmd.vertexCount,
cmd.vertexCount, cmd.instanceCount); cmd.instanceCount);
break; break;
case RenderCommandType::DrawElementsInstanced: case RenderCommandType::DrawElementsInstanced:
device_.drawElementsInstanced(cmd.primitive, cmd.indexCount, device_.drawElementsInstanced(cmd.primitive, cmd.indexCount, cmd.indexType,
cmd.indexType, cmd.indices, cmd.instanceCount); cmd.indices, cmd.instanceCount);
break; break;
default: default:
@ -143,12 +143,12 @@ void RenderContext::setViewport(int32 x, int32 y, int32 width, int32 height) {
device_.setViewport(x, y, width, height); device_.setViewport(x, y, width, height);
} }
void RenderContext::setViewport(const Viewport& vp) { void RenderContext::setViewport(const Viewport &vp) {
viewport_ = vp; viewport_ = vp;
device_.setViewport(vp.x, vp.y, vp.width, vp.height); device_.setViewport(vp.x, vp.y, vp.width, vp.height);
} }
void RenderContext::setScissor(const ScissorRect& rect) { void RenderContext::setScissor(const ScissorRect &rect) {
scissor_ = rect; scissor_ = rect;
device_.setScissorEnabled(rect.enabled); device_.setScissorEnabled(rect.enabled);
if (rect.enabled) { if (rect.enabled) {
@ -166,9 +166,7 @@ void RenderContext::setScissor(int32 x, int32 y, int32 width, int32 height) {
device_.setScissor(x, y, width, height); device_.setScissor(x, y, width, height);
} }
void RenderContext::pushViewport() { void RenderContext::pushViewport() { viewportStack_.push(viewport_); }
viewportStack_.push(viewport_);
}
void RenderContext::popViewport() { void RenderContext::popViewport() {
if (!viewportStack_.empty()) { if (!viewportStack_.empty()) {
@ -177,9 +175,7 @@ void RenderContext::popViewport() {
} }
} }
void RenderContext::pushScissor() { void RenderContext::pushScissor() { scissorStack_.push(scissor_); }
scissorStack_.push(scissor_);
}
void RenderContext::popScissor() { void RenderContext::popScissor() {
if (!scissorStack_.empty()) { if (!scissorStack_.empty()) {
@ -188,21 +184,17 @@ void RenderContext::popScissor() {
} }
} }
void RenderContext::setClearColor(const Color& color) { void RenderContext::setClearColor(const Color &color) { clearColor_ = color; }
clearColor_ = color;
}
void RenderContext::clear(bool color, bool depth, bool stencil) { void RenderContext::clear(bool color, bool depth, bool stencil) {
device_.clear(color, depth, stencil, device_.clear(color, depth, stencil, clearColor_.r, clearColor_.g,
clearColor_.r, clearColor_.g, clearColor_.b, clearColor_.a); clearColor_.b, clearColor_.a);
} }
const RenderStats& RenderContext::stats() const { const RenderStats &RenderContext::stats() const {
return E2D_RENDER_STATS().stats(); return E2D_RENDER_STATS().stats();
} }
void RenderContext::resetStats() { void RenderContext::resetStats() { E2D_RENDER_STATS().reset(); }
E2D_RENDER_STATS().reset();
}
} // namespace extra2d } // namespace extra2d

View File

@ -1,53 +1,114 @@
#include <cstdio>
#include <extra2d/render/render_device.h> #include <extra2d/render/render_device.h>
#include <extra2d/render/render_stats.h> #include <extra2d/render/render_stats.h>
#include <cstdio> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
RenderDevice& RenderDevice::instance() { RenderDevice &RenderDevice::instance() {
static RenderDevice instance; static RenderDevice instance;
return instance; return instance;
} }
bool RenderDevice::init() { bool RenderDevice::init(SDL_Window *window, const RenderDeviceConfig &config) {
if (initialized_) { if (initialized_)
return true; return true;
}
GLint majorVersion = 0; window_ = window;
GLint minorVersion = 0;
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
if (majorVersion < 4 || (majorVersion == 4 && minorVersion < 5)) { if (!initGL(config)) {
printf("OpenGL 4.5 is required, got %d.%d\n", majorVersion, minorVersion);
return false; return false;
} }
queryCaps(); queryCaps();
if (!caps_.dsaSupported) {
printf("OpenGL 4.5 DSA is not supported\n");
return false;
}
glEnable(GL_TEXTURE_2D);
currentBlend_ = BlendState::opaque(); currentBlend_ = BlendState::opaque();
currentDepth_ = DepthState{}; currentDepth_ = DepthState{};
currentRaster_ = RasterState{}; currentRaster_ = RasterState{};
initialized_ = true; initialized_ = true;
E2D_INFO("RenderDevice initialized");
E2D_INFO("OpenGL Version: {}", caps_.version);
E2D_INFO("OpenGL Renderer: {}", caps_.renderer);
E2D_INFO("GLSL Version: {}", caps_.glslVersion);
return true;
}
bool RenderDevice::initGL(const RenderDeviceConfig &config) {
if (config.useES) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
} else {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
SDL_GL_CONTEXT_PROFILE_CORE);
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, config.glMajor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, config.glMinor);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, config.redBits);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, config.greenBits);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, config.blueBits);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, config.alphaBits);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config.depthBits);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config.stencilBits);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, config.doubleBuffer ? 1 : 0);
if (config.msaaSamples > 0) {
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config.msaaSamples);
}
glContext_ = SDL_GL_CreateContext(window_);
if (!glContext_) {
E2D_ERROR("SDL_GL_CreateContext failed: {}", SDL_GetError());
return false;
}
if (SDL_GL_MakeCurrent(window_, glContext_) != 0) {
E2D_ERROR("SDL_GL_MakeCurrent failed: {}", SDL_GetError());
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
if (gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress)) ==
0) {
E2D_ERROR("gladLoadGLLoader failed");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
SDL_GL_SetSwapInterval(config.vsync ? 1 : 0);
return true; return true;
} }
void RenderDevice::shutdown() { void RenderDevice::shutdown() {
if (!initialized_) { if (!initialized_)
return; return;
if (glContext_) {
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
} }
window_ = nullptr;
currentProgram_ = 0; currentProgram_ = 0;
initialized_ = false; initialized_ = false;
E2D_INFO("RenderDevice shutdown");
}
void RenderDevice::swapBuffers() {
if (window_) {
SDL_GL_SwapWindow(window_);
}
}
void RenderDevice::setVSync(bool enabled) {
SDL_GL_SetSwapInterval(enabled ? 1 : 0);
} }
void RenderDevice::queryCaps() { void RenderDevice::queryCaps() {
@ -56,8 +117,10 @@ void RenderDevice::queryCaps() {
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps_.maxVertexAttribs); glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps_.maxVertexAttribs);
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &caps_.maxUniformBlockBindings); glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &caps_.maxUniformBlockBindings);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &caps_.maxUniformBlockSize); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &caps_.maxUniformBlockSize);
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &caps_.maxVertexUniformComponents); glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS,
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &caps_.maxFragmentUniformComponents); &caps_.maxVertexUniformComponents);
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS,
&caps_.maxFragmentUniformComponents);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps_.maxDrawBuffers); glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps_.maxDrawBuffers);
glGetIntegerv(GL_MAX_SAMPLES, &caps_.maxSamples); glGetIntegerv(GL_MAX_SAMPLES, &caps_.maxSamples);
@ -65,31 +128,20 @@ void RenderDevice::queryCaps() {
GLint minorVersion = 0; GLint minorVersion = 0;
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion); glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
glGetIntegerv(GL_MINOR_VERSION, &minorVersion); glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
caps_.dsaSupported = (majorVersion > 4 || (majorVersion == 4 && minorVersion >= 5)); caps_.dsaSupported =
(majorVersion > 4 || (majorVersion == 4 && minorVersion >= 5));
GLint extensions = 0; caps_.renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
glGetIntegerv(GL_NUM_EXTENSIONS, &extensions); caps_.vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
for (GLint i = 0; i < extensions; ++i) { caps_.version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
String ext = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)); caps_.glslVersion =
if (ext.find("compute_shader") != String::npos) { reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
caps_.computeSupported = true;
}
if (ext.find("tessellation_shader") != String::npos) {
caps_.tessellationSupported = true;
}
if (ext.find("geometry_shader") != String::npos) {
caps_.geometryShaderSupported = true;
}
}
caps_.renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
caps_.vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
caps_.version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
caps_.glslVersion = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
} }
std::unique_ptr<Buffer> RenderDevice::createBuffer(BufferType type, BufferUsage usage, std::unique_ptr<Buffer> RenderDevice::createBuffer(BufferType type,
size_t size, const void* data) { BufferUsage usage,
size_t size,
const void *data) {
auto buffer = std::make_unique<Buffer>(); auto buffer = std::make_unique<Buffer>();
if (!buffer->create(type, usage, size, data)) { if (!buffer->create(type, usage, size, data)) {
return nullptr; return nullptr;
@ -97,8 +149,9 @@ std::unique_ptr<Buffer> RenderDevice::createBuffer(BufferType type, BufferUsage
return buffer; return buffer;
} }
std::unique_ptr<VertexBuffer> RenderDevice::createVertexBuffer(BufferUsage usage, size_t size, std::unique_ptr<VertexBuffer>
const void* data) { RenderDevice::createVertexBuffer(BufferUsage usage, size_t size,
const void *data) {
auto buffer = std::make_unique<VertexBuffer>(); auto buffer = std::make_unique<VertexBuffer>();
if (!buffer->create(usage, size, data)) { if (!buffer->create(usage, size, data)) {
return nullptr; return nullptr;
@ -106,8 +159,9 @@ std::unique_ptr<VertexBuffer> RenderDevice::createVertexBuffer(BufferUsage usage
return buffer; return buffer;
} }
std::unique_ptr<IndexBuffer> RenderDevice::createIndexBuffer(BufferUsage usage, size_t size, std::unique_ptr<IndexBuffer> RenderDevice::createIndexBuffer(BufferUsage usage,
const void* data) { size_t size,
const void *data) {
auto buffer = std::make_unique<IndexBuffer>(); auto buffer = std::make_unique<IndexBuffer>();
if (!buffer->create(usage, size, data)) { if (!buffer->create(usage, size, data)) {
return nullptr; return nullptr;
@ -115,8 +169,9 @@ std::unique_ptr<IndexBuffer> RenderDevice::createIndexBuffer(BufferUsage usage,
return buffer; return buffer;
} }
std::unique_ptr<UniformBuffer> RenderDevice::createUniformBuffer(BufferUsage usage, size_t size, std::unique_ptr<UniformBuffer>
const void* data) { RenderDevice::createUniformBuffer(BufferUsage usage, size_t size,
const void *data) {
auto buffer = std::make_unique<UniformBuffer>(); auto buffer = std::make_unique<UniformBuffer>();
if (!buffer->create(usage, size, data)) { if (!buffer->create(usage, size, data)) {
return nullptr; return nullptr;
@ -148,8 +203,8 @@ void RenderDevice::setScissorEnabled(bool enabled) {
} }
} }
void RenderDevice::clear(bool color, bool depth, bool stencil, void RenderDevice::clear(bool color, bool depth, bool stencil, float r, float g,
float r, float g, float b, float a) { float b, float a) {
GLbitfield mask = 0; GLbitfield mask = 0;
if (color) { if (color) {
glClearColor(r, g, b, a); glClearColor(r, g, b, a);
@ -166,7 +221,7 @@ void RenderDevice::clear(bool color, bool depth, bool stencil,
glClear(mask); glClear(mask);
} }
void RenderDevice::setBlendState(const BlendState& state) { void RenderDevice::setBlendState(const BlendState &state) {
if (state.enabled != currentBlend_.enabled) { if (state.enabled != currentBlend_.enabled) {
if (state.enabled) { if (state.enabled) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -181,30 +236,24 @@ void RenderDevice::setBlendState(const BlendState& state) {
state.dstRGB != currentBlend_.dstRGB || state.dstRGB != currentBlend_.dstRGB ||
state.srcAlpha != currentBlend_.srcAlpha || state.srcAlpha != currentBlend_.srcAlpha ||
state.dstAlpha != currentBlend_.dstAlpha) { state.dstAlpha != currentBlend_.dstAlpha) {
glBlendFuncSeparate( glBlendFuncSeparate(static_cast<GLenum>(state.srcRGB) + GL_ZERO,
static_cast<GLenum>(state.srcRGB) + GL_ZERO,
static_cast<GLenum>(state.dstRGB) + GL_ZERO, static_cast<GLenum>(state.dstRGB) + GL_ZERO,
static_cast<GLenum>(state.srcAlpha) + GL_ZERO, static_cast<GLenum>(state.srcAlpha) + GL_ZERO,
static_cast<GLenum>(state.dstAlpha) + GL_ZERO static_cast<GLenum>(state.dstAlpha) + GL_ZERO);
);
E2D_RENDER_STATS().addStateChange(); E2D_RENDER_STATS().addStateChange();
} }
if (state.opRGB != currentBlend_.opRGB || state.opAlpha != currentBlend_.opAlpha) { if (state.opRGB != currentBlend_.opRGB ||
glBlendEquationSeparate( state.opAlpha != currentBlend_.opAlpha) {
static_cast<GLenum>(state.opRGB) + GL_FUNC_ADD, glBlendEquationSeparate(static_cast<GLenum>(state.opRGB) + GL_FUNC_ADD,
static_cast<GLenum>(state.opAlpha) + GL_FUNC_ADD static_cast<GLenum>(state.opAlpha) + GL_FUNC_ADD);
);
E2D_RENDER_STATS().addStateChange(); E2D_RENDER_STATS().addStateChange();
} }
if (state.colorWriteMask != currentBlend_.colorWriteMask) { if (state.colorWriteMask != currentBlend_.colorWriteMask) {
glColorMask( glColorMask(
(state.colorWriteMask & 0x1) != 0, (state.colorWriteMask & 0x1) != 0, (state.colorWriteMask & 0x2) != 0,
(state.colorWriteMask & 0x2) != 0, (state.colorWriteMask & 0x4) != 0, (state.colorWriteMask & 0x8) != 0);
(state.colorWriteMask & 0x4) != 0,
(state.colorWriteMask & 0x8) != 0
);
E2D_RENDER_STATS().addStateChange(); E2D_RENDER_STATS().addStateChange();
} }
} }
@ -212,7 +261,7 @@ void RenderDevice::setBlendState(const BlendState& state) {
currentBlend_ = state; currentBlend_ = state;
} }
void RenderDevice::setDepthState(const DepthState& state) { void RenderDevice::setDepthState(const DepthState &state) {
if (state.testEnabled != currentDepth_.testEnabled) { if (state.testEnabled != currentDepth_.testEnabled) {
if (state.testEnabled) { if (state.testEnabled) {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
@ -235,7 +284,7 @@ void RenderDevice::setDepthState(const DepthState& state) {
currentDepth_ = state; currentDepth_ = state;
} }
void RenderDevice::setRasterState(const RasterState& state) { void RenderDevice::setRasterState(const RasterState &state) {
if (state.cullMode != currentRaster_.cullMode) { if (state.cullMode != currentRaster_.cullMode) {
if (state.cullMode == CullMode::None) { if (state.cullMode == CullMode::None) {
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
@ -247,9 +296,11 @@ void RenderDevice::setRasterState(const RasterState& state) {
} }
if (state.polygonMode != currentRaster_.polygonMode) { if (state.polygonMode != currentRaster_.polygonMode) {
glPolygonMode(GL_FRONT_AND_BACK, glPolygonMode(
state.polygonMode == PolygonMode::Line ? GL_LINE : GL_FRONT_AND_BACK,
(state.polygonMode == PolygonMode::Point ? GL_POINT : GL_FILL)); state.polygonMode == PolygonMode::Line
? GL_LINE
: (state.polygonMode == PolygonMode::Point ? GL_POINT : GL_FILL));
E2D_RENDER_STATS().addStateChange(); E2D_RENDER_STATS().addStateChange();
} }
@ -279,7 +330,7 @@ void RenderDevice::drawArrays(PrimitiveType mode, int32 first, int32 count) {
} }
void RenderDevice::drawElements(PrimitiveType mode, int32 count, IndexType type, void RenderDevice::drawElements(PrimitiveType mode, int32 count, IndexType type,
const void* indices) { const void *indices) {
glDrawElements(glPrimitiveType(mode), count, glIndexType(type), indices); glDrawElements(glPrimitiveType(mode), count, glIndexType(type), indices);
E2D_RENDER_STATS().addDrawCall(count, count / 3); E2D_RENDER_STATS().addDrawCall(count, count / 3);
} }
@ -287,35 +338,50 @@ void RenderDevice::drawElements(PrimitiveType mode, int32 count, IndexType type,
void RenderDevice::drawArraysInstanced(PrimitiveType mode, int32 first, void RenderDevice::drawArraysInstanced(PrimitiveType mode, int32 first,
int32 count, int32 instanceCount) { int32 count, int32 instanceCount) {
glDrawArraysInstanced(glPrimitiveType(mode), first, count, instanceCount); glDrawArraysInstanced(glPrimitiveType(mode), first, count, instanceCount);
E2D_RENDER_STATS().addDrawCall(count * instanceCount, (count / 3) * instanceCount); E2D_RENDER_STATS().addDrawCall(count * instanceCount,
(count / 3) * instanceCount);
} }
void RenderDevice::drawElementsInstanced(PrimitiveType mode, int32 count, IndexType type, void RenderDevice::drawElementsInstanced(PrimitiveType mode, int32 count,
const void* indices, int32 instanceCount) { IndexType type, const void *indices,
int32 instanceCount) {
glDrawElementsInstanced(glPrimitiveType(mode), count, glIndexType(type), glDrawElementsInstanced(glPrimitiveType(mode), count, glIndexType(type),
indices, instanceCount); indices, instanceCount);
E2D_RENDER_STATS().addDrawCall(count * instanceCount, (count / 3) * instanceCount); E2D_RENDER_STATS().addDrawCall(count * instanceCount,
(count / 3) * instanceCount);
} }
GLenum RenderDevice::glPrimitiveType(PrimitiveType mode) const { GLenum RenderDevice::glPrimitiveType(PrimitiveType mode) const {
switch (mode) { switch (mode) {
case PrimitiveType::Points: return GL_POINTS; case PrimitiveType::Points:
case PrimitiveType::Lines: return GL_LINES; return GL_POINTS;
case PrimitiveType::LineStrip: return GL_LINE_STRIP; case PrimitiveType::Lines:
case PrimitiveType::LineLoop: return GL_LINE_LOOP; return GL_LINES;
case PrimitiveType::Triangles: return GL_TRIANGLES; case PrimitiveType::LineStrip:
case PrimitiveType::TriangleStrip: return GL_TRIANGLE_STRIP; return GL_LINE_STRIP;
case PrimitiveType::TriangleFan: return GL_TRIANGLE_FAN; case PrimitiveType::LineLoop:
default: return GL_TRIANGLES; return GL_LINE_LOOP;
case PrimitiveType::Triangles:
return GL_TRIANGLES;
case PrimitiveType::TriangleStrip:
return GL_TRIANGLE_STRIP;
case PrimitiveType::TriangleFan:
return GL_TRIANGLE_FAN;
default:
return GL_TRIANGLES;
} }
} }
GLenum RenderDevice::glIndexType(IndexType type) const { GLenum RenderDevice::glIndexType(IndexType type) const {
switch (type) { switch (type) {
case IndexType::UInt8: return GL_UNSIGNED_BYTE; case IndexType::UInt8:
case IndexType::UInt16: return GL_UNSIGNED_SHORT; return GL_UNSIGNED_BYTE;
case IndexType::UInt32: return GL_UNSIGNED_INT; case IndexType::UInt16:
default: return GL_UNSIGNED_INT; return GL_UNSIGNED_SHORT;
case IndexType::UInt32:
return GL_UNSIGNED_INT;
default:
return GL_UNSIGNED_INT;
} }
} }

View File

@ -0,0 +1,219 @@
#include <extra2d/render/renderer.h>
#include <extra2d/render/render_context.h>
namespace extra2d {
Renderer::Renderer() = default;
Renderer::~Renderer() = default;
bool Renderer::init() {
shapeRenderer_ = std::make_unique<ShapeRenderer>();
if (!shapeRenderer_->init()) {
return false;
}
spriteRenderer_ = std::make_unique<SpriteRenderer>();
if (!spriteRenderer_->init()) {
shapeRenderer_->shutdown();
return false;
}
return true;
}
void Renderer::shutdown() {
if (spriteRenderer_) {
spriteRenderer_->shutdown();
spriteRenderer_.reset();
}
if (shapeRenderer_) {
shapeRenderer_->shutdown();
shapeRenderer_.reset();
}
}
void Renderer::beginFrame(const Color& clearColor) {
auto& ctx = RenderContext::instance();
ctx.beginFrame();
ctx.setClearColor(clearColor);
ctx.clear(true, true, false);
inFrame_ = true;
}
void Renderer::endFrame() {
if (!inFrame_) {
return;
}
if (spriteRenderer_) {
spriteRenderer_->end();
}
if (shapeRenderer_) {
shapeRenderer_->end();
}
auto& ctx = RenderContext::instance();
ctx.endFrame();
inFrame_ = false;
}
void Renderer::setViewProjection(const glm::mat4& viewProjection) {
viewProjection_ = viewProjection;
if (shapeRenderer_) {
shapeRenderer_->end();
shapeRenderer_->begin(viewProjection_);
}
if (spriteRenderer_) {
spriteRenderer_->end();
spriteRenderer_->begin(viewProjection_);
}
}
void Renderer::drawLine(const Vec2& start, const Vec2& end,
const Color& color, float width) {
if (shapeRenderer_) {
shapeRenderer_->drawLine(start.x, start.y, end.x, end.y,
toVec4(color), width);
}
}
void Renderer::drawRect(const Rect& rect, const Color& color, float width) {
if (shapeRenderer_) {
shapeRenderer_->drawRect(rect.origin.x, rect.origin.y,
rect.size.width, rect.size.height,
toVec4(color), width);
}
}
void Renderer::fillRect(const Rect& rect, const Color& color) {
if (shapeRenderer_) {
shapeRenderer_->fillRect(rect.origin.x, rect.origin.y,
rect.size.width, rect.size.height,
toVec4(color));
}
}
void Renderer::drawCircle(const Vec2& center, float radius, const Color& color,
int segments, float width) {
if (shapeRenderer_) {
shapeRenderer_->drawCircle(center.x, center.y, radius,
toVec4(color), width, segments);
}
}
void Renderer::fillCircle(const Vec2& center, float radius,
const Color& color, int segments) {
if (shapeRenderer_) {
shapeRenderer_->fillCircle(center.x, center.y, radius,
toVec4(color), segments);
}
}
void Renderer::drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
const Color& color, float width) {
if (shapeRenderer_) {
shapeRenderer_->drawLine(p1.x, p1.y, p2.x, p2.y, toVec4(color), width);
shapeRenderer_->drawLine(p2.x, p2.y, p3.x, p3.y, toVec4(color), width);
shapeRenderer_->drawLine(p3.x, p3.y, p1.x, p1.y, toVec4(color), width);
}
}
void Renderer::fillTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
const Color& color) {
if (shapeRenderer_) {
shapeRenderer_->fillTriangle(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y,
toVec4(color));
}
}
void Renderer::drawPolygon(const std::vector<Vec2>& points, const Color& color,
float width) {
if (shapeRenderer_ && points.size() >= 2) {
std::vector<glm::vec2> glmPoints;
glmPoints.reserve(points.size());
for (const auto& p : points) {
glmPoints.emplace_back(p.x, p.y);
}
shapeRenderer_->drawPolygon(glmPoints.data(), glmPoints.size(),
toVec4(color), width);
}
}
void Renderer::fillPolygon(const std::vector<Vec2>& points, const Color& color) {
if (shapeRenderer_ && points.size() >= 3) {
std::vector<glm::vec2> glmPoints;
glmPoints.reserve(points.size());
for (const auto& p : points) {
glmPoints.emplace_back(p.x, p.y);
}
shapeRenderer_->fillPolygon(glmPoints.data(), glmPoints.size(),
toVec4(color));
}
}
void Renderer::drawSprite(const Texture& texture, const Rect& destRect,
const Rect& srcRect, const Color& color,
float rotation, const Vec2& anchor) {
if (spriteRenderer_ && texture.isValid()) {
SpriteData data;
data.position = glm::vec2(destRect.origin.x, destRect.origin.y);
data.size = glm::vec2(destRect.size.width, destRect.size.height);
data.anchor = glm::vec2(anchor.x, anchor.y);
data.color = toVec4(color);
data.texRect = glm::vec4(
srcRect.origin.x / static_cast<float>(texture.width()),
srcRect.origin.y / static_cast<float>(texture.height()),
srcRect.size.width / static_cast<float>(texture.width()),
srcRect.size.height / static_cast<float>(texture.height())
);
data.rotation = rotation;
spriteRenderer_->draw(texture.glHandle(), data);
}
}
void Renderer::drawSprite(const Texture& texture, const Vec2& position,
const Color& color) {
if (texture.isValid()) {
Rect destRect(position.x, position.y,
static_cast<float>(texture.width()),
static_cast<float>(texture.height()));
Rect srcRect(0, 0, static_cast<float>(texture.width()),
static_cast<float>(texture.height()));
drawSprite(texture, destRect, srcRect, color);
}
}
void Renderer::drawSprite(const Texture& texture, const Vec2& position,
const Vec2& scale, const Color& color) {
if (texture.isValid()) {
Rect destRect(position.x, position.y,
static_cast<float>(texture.width()) * scale.x,
static_cast<float>(texture.height()) * scale.y);
Rect srcRect(0, 0, static_cast<float>(texture.width()),
static_cast<float>(texture.height()));
drawSprite(texture, destRect, srcRect, color);
}
}
uint32 Renderer::drawCalls() const {
uint32 calls = 0;
if (shapeRenderer_) {
calls += shapeRenderer_->drawCalls();
}
if (spriteRenderer_) {
calls += spriteRenderer_->drawCalls();
}
return calls;
}
uint32 Renderer::spriteCount() const {
return spriteRenderer_ ? spriteRenderer_->spriteCount() : 0;
}
glm::vec4 Renderer::toVec4(const Color& color) const {
return glm::vec4(color.r, color.g, color.b, color.a);
}
} // namespace extra2d

View File

@ -0,0 +1,189 @@
#include <extra2d/render/texture.h>
#include <glad/glad.h>
namespace extra2d {
Texture::~Texture() {
if (glHandle_ != 0) {
glDeleteTextures(1, &glHandle_);
glHandle_ = 0;
}
}
std::unique_ptr<Texture> Texture::create(const u8 *data,
const TextureConfig &config) {
auto texture = std::make_unique<Texture>();
if (!texture->init(data, config)) {
return nullptr;
}
return texture;
}
std::unique_ptr<Texture> Texture::createEmpty(int width, int height,
int channels) {
TextureConfig config;
config.width = width;
config.height = height;
config.channels = channels;
return create(nullptr, config);
}
bool Texture::init(const u8 *data, const TextureConfig &config) {
width_ = config.width;
height_ = config.height;
channels_ = config.channels;
glCreateTextures(GL_TEXTURE_2D, 1, &glHandle_);
if (glHandle_ == 0) {
return false;
}
glTextureStorage2D(glHandle_, 1, glInternalFormat(), width_, height_);
if (data != nullptr) {
glTextureSubImage2D(glHandle_, 0, 0, 0, width_, height_, glFormat(),
GL_UNSIGNED_BYTE, data);
}
setFilter(config.filter);
setWrap(config.wrap);
if (config.generateMips) {
glGenerateTextureMipmap(glHandle_);
}
return true;
}
void Texture::update(const u8 *data, int width, int height, int channels) {
if (glHandle_ == 0) {
return;
}
if (width != width_ || height != height_ || channels != channels_) {
glDeleteTextures(1, &glHandle_);
width_ = width;
height_ = height;
channels_ = channels;
glCreateTextures(GL_TEXTURE_2D, 1, &glHandle_);
glTextureStorage2D(glHandle_, 1, glInternalFormat(), width_, height_);
}
glTextureSubImage2D(glHandle_, 0, 0, 0, width_, height_, glFormat(),
GL_UNSIGNED_BYTE, data);
}
void Texture::updateSubRegion(const u8 *data, int x, int y, int width,
int height) {
if (glHandle_ == 0) {
return;
}
glTextureSubImage2D(glHandle_, 0, x, y, width, height, glFormat(),
GL_UNSIGNED_BYTE, data);
}
void Texture::bind(uint32 unit) const {
if (glHandle_ != 0) {
glBindTextureUnit(unit, glHandle_);
}
}
void Texture::unbind() const { glBindTextureUnit(0, 0); }
void Texture::setFilter(FilterMode filter) {
if (glHandle_ == 0) {
return;
}
GLenum minFilter, magFilter;
switch (filter) {
case FilterMode::Nearest:
minFilter = GL_NEAREST;
magFilter = GL_NEAREST;
break;
case FilterMode::NearestMipmapNearest:
minFilter = GL_NEAREST_MIPMAP_NEAREST;
magFilter = GL_NEAREST;
break;
case FilterMode::LinearMipmapNearest:
minFilter = GL_LINEAR_MIPMAP_NEAREST;
magFilter = GL_LINEAR;
break;
case FilterMode::NearestMipmapLinear:
minFilter = GL_NEAREST_MIPMAP_LINEAR;
magFilter = GL_NEAREST;
break;
case FilterMode::LinearMipmapLinear:
minFilter = GL_LINEAR_MIPMAP_LINEAR;
magFilter = GL_LINEAR;
break;
case FilterMode::Linear:
default:
minFilter = GL_LINEAR;
magFilter = GL_LINEAR;
break;
}
glTextureParameteri(glHandle_, GL_TEXTURE_MIN_FILTER, minFilter);
glTextureParameteri(glHandle_, GL_TEXTURE_MAG_FILTER, magFilter);
}
void Texture::setWrap(WrapMode wrap) {
if (glHandle_ == 0) {
return;
}
GLenum glWrap;
switch (wrap) {
case WrapMode::Repeat:
glWrap = GL_REPEAT;
break;
case WrapMode::Mirror:
glWrap = GL_MIRRORED_REPEAT;
break;
case WrapMode::MirrorClamp:
glWrap = GL_MIRROR_CLAMP_TO_EDGE;
break;
case WrapMode::Border:
glWrap = GL_CLAMP_TO_BORDER;
break;
case WrapMode::Clamp:
default:
glWrap = GL_CLAMP_TO_EDGE;
break;
}
glTextureParameteri(glHandle_, GL_TEXTURE_WRAP_S, glWrap);
glTextureParameteri(glHandle_, GL_TEXTURE_WRAP_T, glWrap);
}
GLenum Texture::glInternalFormat() const {
switch (channels_) {
case 1:
return GL_R8;
case 2:
return GL_RG8;
case 3:
return GL_RGB8;
case 4:
default:
return GL_RGBA8;
}
}
GLenum Texture::glFormat() const {
switch (channels_) {
case 1:
return GL_RED;
case 2:
return GL_RG;
case 3:
return GL_RGB;
case 4:
default:
return GL_RGBA;
}
}
} // namespace extra2d

View File

@ -1,17 +1,18 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <extra2d/graphics/render_command.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
Node::Node() = default; Node::Node() = default;
Node::~Node() { removeAllChildren(); } Node::~Node() {
removeAllChildren();
}
void Node::addChild(Ptr<Node> child) { void Node::addChild(Ref<Node> child) {
if (!child || child.get() == this) { if (!child || child.get() == this) {
return; return;
} }
@ -21,7 +22,6 @@ void Node::addChild(Ptr<Node> child) {
children_.push_back(child); children_.push_back(child);
childrenOrderDirty_ = true; childrenOrderDirty_ = true;
// 更新索引
if (!child->getName().empty()) { if (!child->getName().empty()) {
nameIndex_[child->getName()] = child; nameIndex_[child->getName()] = child;
} }
@ -37,14 +37,13 @@ void Node::addChild(Ptr<Node> child) {
} }
} }
void Node::addChildren(std::vector<Ptr<Node>> &&children) { void Node::addChildren(std::vector<Ref<Node>>&& children) {
// 预留空间,避免多次扩容
size_t newSize = children_.size() + children.size(); size_t newSize = children_.size() + children.size();
if (newSize > children_.capacity()) { if (newSize > children_.capacity()) {
children_.reserve(newSize); children_.reserve(newSize);
} }
for (auto &child : children) { for (auto& child : children) {
if (!child || child.get() == this) { if (!child || child.get() == this) {
continue; continue;
} }
@ -53,7 +52,6 @@ void Node::addChildren(std::vector<Ptr<Node>> &&children) {
child->parent_ = weak_from_this(); child->parent_ = weak_from_this();
children_.push_back(child); children_.push_back(child);
// 更新索引
if (!child->getName().empty()) { if (!child->getName().empty()) {
nameIndex_[child->getName()] = child; nameIndex_[child->getName()] = child;
} }
@ -74,20 +72,19 @@ void Node::addChildren(std::vector<Ptr<Node>> &&children) {
} }
} }
void Node::removeChild(Ptr<Node> child) { void Node::removeChild(Ref<Node> child) {
if (!child) if (!child) {
return; return;
}
auto it = std::find(children_.begin(), children_.end(), child); auto it = std::find(children_.begin(), children_.end(), child);
if (it != children_.end()) { if (it != children_.end()) {
// 始终从空间索引中移除(无论 running_ 状态)
// 这确保节点被正确清理
(*it)->onDetachFromScene(); (*it)->onDetachFromScene();
if (running_) { if (running_) {
(*it)->onExit(); (*it)->onExit();
} }
// 从索引中移除
if (!(*it)->getName().empty()) { if (!(*it)->getName().empty()) {
nameIndex_.erase((*it)->getName()); nameIndex_.erase((*it)->getName());
} }
@ -99,7 +96,7 @@ void Node::removeChild(Ptr<Node> child) {
} }
} }
void Node::removeChildByName(const std::string &name) { void Node::removeChildByName(const std::string& name) {
auto child = getChildByName(name); auto child = getChildByName(name);
if (child) { if (child) {
removeChild(child); removeChild(child);
@ -109,12 +106,10 @@ void Node::removeChildByName(const std::string &name) {
void Node::removeFromParent() { void Node::removeFromParent() {
auto p = parent_.lock(); auto p = parent_.lock();
if (p) { if (p) {
// 安全获取 shared_ptr避免在对象未由 shared_ptr 管理时崩溃 Ref<Node> self;
Ptr<Node> self;
try { try {
self = shared_from_this(); self = shared_from_this();
} catch (const std::bad_weak_ptr &) { } catch (const std::bad_weak_ptr&) {
// 对象不是由 shared_ptr 管理的,直接重置父节点引用
parent_.reset(); parent_.reset();
return; return;
} }
@ -123,7 +118,7 @@ void Node::removeFromParent() {
} }
void Node::removeAllChildren() { void Node::removeAllChildren() {
for (auto &child : children_) { for (auto& child : children_) {
if (running_) { if (running_) {
child->onDetachFromScene(); child->onDetachFromScene();
child->onExit(); child->onExit();
@ -135,8 +130,7 @@ void Node::removeAllChildren() {
tagIndex_.clear(); tagIndex_.clear();
} }
Ptr<Node> Node::getChildByName(const std::string &name) const { Ref<Node> Node::getChildByName(const std::string& name) const {
// 使用哈希索引O(1) 查找
auto it = nameIndex_.find(name); auto it = nameIndex_.find(name);
if (it != nameIndex_.end()) { if (it != nameIndex_.end()) {
return it->second.lock(); return it->second.lock();
@ -144,8 +138,7 @@ Ptr<Node> Node::getChildByName(const std::string &name) const {
return nullptr; return nullptr;
} }
Ptr<Node> Node::getChildByTag(int tag) const { Ref<Node> Node::getChildByTag(int tag) const {
// 使用哈希索引O(1) 查找
auto it = tagIndex_.find(tag); auto it = tagIndex_.find(tag);
if (it != tagIndex_.end()) { if (it != tagIndex_.end()) {
return it->second.lock(); return it->second.lock();
@ -153,13 +146,15 @@ Ptr<Node> Node::getChildByTag(int tag) const {
return nullptr; return nullptr;
} }
void Node::setPosition(const Vec2 &pos) { void Node::setPosition(const Vec2& pos) {
position_ = pos; position_ = pos;
markTransformDirty(); markTransformDirty();
updateSpatialIndex(); updateSpatialIndex();
} }
void Node::setPosition(float x, float y) { setPosition(Vec2(x, y)); } void Node::setPosition(float x, float y) {
setPosition(Vec2(x, y));
}
void Node::setRotation(float degrees) { void Node::setRotation(float degrees) {
rotation_ = degrees; rotation_ = degrees;
@ -167,41 +162,57 @@ void Node::setRotation(float degrees) {
updateSpatialIndex(); updateSpatialIndex();
} }
void Node::setScale(const Vec2 &scale) { void Node::setScale(const Vec2& scale) {
scale_ = scale; scale_ = scale;
markTransformDirty(); markTransformDirty();
updateSpatialIndex(); updateSpatialIndex();
} }
void Node::setScale(float scale) { setScale(Vec2(scale, scale)); } void Node::setScale(float scale) {
setScale(Vec2(scale, scale));
}
void Node::setScale(float x, float y) { setScale(Vec2(x, y)); } void Node::setScale(float x, float y) {
setScale(Vec2(x, y));
}
void Node::setAnchor(const Vec2 &anchor) { void Node::setAnchor(const Vec2& anchor) {
anchor_ = anchor; anchor_ = anchor;
markTransformDirty(); markTransformDirty();
} }
void Node::setAnchor(float x, float y) { setAnchor(Vec2(x, y)); } void Node::setAnchor(float x, float y) {
setAnchor(Vec2(x, y));
}
void Node::setSkew(const Vec2 &skew) { void Node::setSkew(const Vec2& skew) {
skew_ = skew; skew_ = skew;
markTransformDirty(); markTransformDirty();
} }
void Node::setSkew(float x, float y) { setSkew(Vec2(x, y)); } void Node::setSkew(float x, float y) {
setSkew(Vec2(x, y));
}
void Node::setOpacity(float opacity) { void Node::setOpacity(float opacity) {
opacity_ = std::clamp(opacity, 0.0f, 1.0f); opacity_ = std::clamp(opacity, 0.0f, 1.0f);
} }
void Node::setVisible(bool visible) { visible_ = visible; } void Node::setVisible(bool visible) {
visible_ = visible;
}
void Node::setColor(const Color3B &color) { color_ = color; } void Node::setColor(const Color3B& color) {
color_ = color;
}
void Node::setFlipX(bool flipX) { flipX_ = flipX; } void Node::setFlipX(bool flipX) {
flipX_ = flipX;
}
void Node::setFlipY(bool flipY) { flipY_ = flipY; } void Node::setFlipY(bool flipY) {
flipY_ = flipY;
}
void Node::setZOrder(int zOrder) { void Node::setZOrder(int zOrder) {
if (zOrder_ != zOrder) { if (zOrder_ != zOrder) {
@ -210,13 +221,12 @@ void Node::setZOrder(int zOrder) {
} }
} }
Vec2 Node::convertToWorldSpace(const Vec2 &localPos) const { Vec2 Node::convertToWorldSpace(const Vec2& localPos) const {
glm::vec4 worldPos = glm::vec4 worldPos = getWorldTransform() * glm::vec4(localPos.x, localPos.y, 0.0f, 1.0f);
getWorldTransform() * glm::vec4(localPos.x, localPos.y, 0.0f, 1.0f);
return Vec2(worldPos.x, worldPos.y); return Vec2(worldPos.x, worldPos.y);
} }
Vec2 Node::convertToNodeSpace(const Vec2 &worldPos) const { Vec2 Node::convertToNodeSpace(const Vec2& worldPos) const {
glm::mat4 invWorld = glm::inverse(getWorldTransform()); glm::mat4 invWorld = glm::inverse(getWorldTransform());
glm::vec4 localPos = invWorld * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f); glm::vec4 localPos = invWorld * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f);
return Vec2(localPos.x, localPos.y); return Vec2(localPos.x, localPos.y);
@ -226,7 +236,6 @@ glm::mat4 Node::getLocalTransform() const {
if (transformDirty_) { if (transformDirty_) {
localTransform_ = glm::mat4(1.0f); localTransform_ = glm::mat4(1.0f);
// T - R - S order
localTransform_ = glm::translate(localTransform_, localTransform_ = glm::translate(localTransform_,
glm::vec3(position_.x, position_.y, 0.0f)); glm::vec3(position_.x, position_.y, 0.0f));
@ -242,11 +251,7 @@ glm::mat4 Node::getLocalTransform() const {
localTransform_ *= skewMatrix; localTransform_ *= skewMatrix;
} }
localTransform_ = localTransform_ = glm::scale(localTransform_, glm::vec3(scale_.x, scale_.y, 1.0f));
glm::scale(localTransform_, glm::vec3(scale_.x, scale_.y, 1.0f));
// 注意:锚点偏移在渲染时处理,不在本地变换中处理
// 这样可以避免锚点偏移被父节点的缩放影响
transformDirty_ = false; transformDirty_ = false;
} }
@ -255,20 +260,17 @@ glm::mat4 Node::getLocalTransform() const {
glm::mat4 Node::getWorldTransform() const { glm::mat4 Node::getWorldTransform() const {
if (worldTransformDirty_) { if (worldTransformDirty_) {
// 使用线程局部存储的固定数组,避免每帧内存分配 thread_local std::array<const Node*, 256> nodeChainCache;
// 限制最大深度为 256 层,足以覆盖绝大多数场景
thread_local std::array<const Node *, 256> nodeChainCache;
thread_local size_t chainCount = 0; thread_local size_t chainCount = 0;
chainCount = 0; chainCount = 0;
const Node *current = this; const Node* current = this;
while (current && chainCount < nodeChainCache.size()) { while (current && chainCount < nodeChainCache.size()) {
nodeChainCache[chainCount++] = current; nodeChainCache[chainCount++] = current;
auto p = current->parent_.lock(); auto p = current->parent_.lock();
current = p.get(); current = p.get();
} }
// 从根节点开始计算
glm::mat4 transform = glm::mat4(1.0f); glm::mat4 transform = glm::mat4(1.0f);
for (size_t i = chainCount; i > 0; --i) { for (size_t i = chainCount; i > 0; --i) {
transform = transform * nodeChainCache[i - 1]->getLocalTransform(); transform = transform * nodeChainCache[i - 1]->getLocalTransform();
@ -280,53 +282,46 @@ glm::mat4 Node::getWorldTransform() const {
} }
void Node::markTransformDirty() { void Node::markTransformDirty() {
// 避免重复标记,提高性能
if (!transformDirty_ || !worldTransformDirty_) { if (!transformDirty_ || !worldTransformDirty_) {
transformDirty_ = true; transformDirty_ = true;
worldTransformDirty_ = true; worldTransformDirty_ = true;
// 递归标记所有子节点 for (auto& child : children_) {
for (auto &child : children_) {
child->markTransformDirty(); child->markTransformDirty();
} }
} }
} }
void Node::batchUpdateTransforms() { void Node::batchUpdateTransforms() {
// 如果本地变换脏了,先计算本地变换
if (transformDirty_) { if (transformDirty_) {
(void)getLocalTransform(); // 这会计算并缓存本地变换 (void)getLocalTransform();
} }
// 如果世界变换脏了,需要重新计算
if (worldTransformDirty_) { if (worldTransformDirty_) {
auto parent = parent_.lock(); auto parent = parent_.lock();
if (parent) { if (parent) {
// 使用父节点的世界变换(确保父节点已经更新)
worldTransform_ = parent->getWorldTransform() * localTransform_; worldTransform_ = parent->getWorldTransform() * localTransform_;
} else { } else {
// 根节点
worldTransform_ = localTransform_; worldTransform_ = localTransform_;
} }
worldTransformDirty_ = false; worldTransformDirty_ = false;
} }
// 递归更新子节点 for (auto& child : children_) {
for (auto &child : children_) {
child->batchUpdateTransforms(); child->batchUpdateTransforms();
} }
} }
void Node::onEnter() { void Node::onEnter() {
running_ = true; running_ = true;
for (auto &child : children_) { for (auto& child : children_) {
child->onEnter(); child->onEnter();
} }
} }
void Node::onExit() { void Node::onExit() {
running_ = false; running_ = false;
for (auto &child : children_) { for (auto& child : children_) {
child->onExit(); child->onExit();
} }
} }
@ -334,54 +329,49 @@ void Node::onExit() {
void Node::onUpdate(float dt) { void Node::onUpdate(float dt) {
onUpdateNode(dt); onUpdateNode(dt);
// Update children for (auto& child : children_) {
for (auto &child : children_) {
child->onUpdate(dt); child->onUpdate(dt);
} }
} }
void Node::onRender(Renderer &renderer) { void Node::onRender(Renderer& renderer) {
if (!visible_) if (!visible_) {
return; return;
}
onDraw(renderer); onDraw(renderer);
for (auto &child : children_) { for (auto& child : children_) {
child->onRender(renderer); child->onRender(renderer);
} }
} }
void Node::onAttachToScene(Scene *scene) { void Node::onAttachToScene(Scene* scene) {
scene_ = scene; scene_ = scene;
// 添加到场景的空间索引
if (spatialIndexed_ && scene_) { if (spatialIndexed_ && scene_) {
lastSpatialBounds_ = Rect(); lastSpatialBounds_ = Rect();
updateSpatialIndex(); updateSpatialIndex();
} }
for (auto &child : children_) { for (auto& child : children_) {
child->onAttachToScene(scene); child->onAttachToScene(scene);
} }
} }
void Node::onDetachFromScene() { void Node::onDetachFromScene() {
// 从场景的空间索引移除
// 注意:即使 lastSpatialBounds_ 为空也要尝试移除,
// 因为节点可能通过其他方式被插入到空间索引中
if (spatialIndexed_ && scene_) { if (spatialIndexed_ && scene_) {
scene_->removeNodeFromSpatialIndex(this); scene_->removeNodeFromSpatialIndex(this);
lastSpatialBounds_ = Rect(); lastSpatialBounds_ = Rect();
} }
scene_ = nullptr; scene_ = nullptr;
for (auto &child : children_) { for (auto& child : children_) {
child->onDetachFromScene(); child->onDetachFromScene();
} }
} }
Rect Node::getBoundingBox() const { Rect Node::getBoundingBox() const {
// 默认返回一个以位置为中心的点矩形
return Rect(position_.x, position_.y, 0, 0); return Rect(position_.x, position_.y, 0, 0);
} }
@ -397,9 +387,11 @@ void Node::updateSpatialIndex() {
} }
} }
void Node::update(float dt) { onUpdate(dt); } void Node::update(float dt) {
onUpdate(dt);
}
void Node::render(Renderer &renderer) { void Node::render(Renderer& renderer) {
if (childrenOrderDirty_) { if (childrenOrderDirty_) {
sortChildren(); sortChildren();
} }
@ -407,17 +399,13 @@ void Node::render(Renderer &renderer) {
} }
void Node::sortChildren() { void Node::sortChildren() {
// 使用插入排序优化小范围更新场景
// 插入排序在大部分已有序的情况下性能接近O(n)
size_t n = children_.size(); size_t n = children_.size();
if (n <= 1) { if (n <= 1) {
childrenOrderDirty_ = false; childrenOrderDirty_ = false;
return; return;
} }
// 小数组使用插入排序大数组使用std::sort
if (n < 32) { if (n < 32) {
// 插入排序
for (size_t i = 1; i < n; ++i) { for (size_t i = 1; i < n; ++i) {
auto key = children_[i]; auto key = children_[i];
int keyZOrder = key->getZOrder(); int keyZOrder = key->getZOrder();
@ -430,9 +418,8 @@ void Node::sortChildren() {
children_[j + 1] = key; children_[j + 1] = key;
} }
} else { } else {
// 大数组使用标准排序
std::sort(children_.begin(), children_.end(), std::sort(children_.begin(), children_.end(),
[](const Ptr<Node> &a, const Ptr<Node> &b) { [](const Ref<Node>& a, const Ref<Node>& b) {
return a->getZOrder() < b->getZOrder(); return a->getZOrder() < b->getZOrder();
}); });
} }
@ -440,22 +427,4 @@ void Node::sortChildren() {
childrenOrderDirty_ = false; childrenOrderDirty_ = false;
} }
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 } // namespace extra2d

View File

@ -1,45 +1,48 @@
#include <extra2d/graphics/render_command.h> #include <extra2d/render/renderer.h>
#include <extra2d/graphics/renderer.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
Scene::Scene() { defaultCamera_ = makePtr<Camera>(); } Scene::Scene() {
defaultCamera_ = ptr::make<Camera>();
}
void Scene::setCamera(Ptr<Camera> camera) { camera_ = camera; } void Scene::setCamera(Ref<Camera> camera) {
camera_ = camera;
}
void Scene::setViewportSize(float width, float height) { void Scene::setViewportSize(float width, float height) {
viewportSize_ = Size(width, height); viewportSize_ = Size(width, height);
if (defaultCamera_) { if (defaultCamera_) {
defaultCamera_->setViewport(0, width, height, 0); defaultCamera_->setViewport(0, static_cast<int>(width), static_cast<int>(height), 0);
} else if (camera_) { } else if (camera_) {
camera_->setViewport(0, width, height, 0); camera_->setViewport(0, static_cast<int>(width), static_cast<int>(height), 0);
} }
} }
void Scene::setViewportSize(const Size &size) { void Scene::setViewportSize(const Size& size) {
setViewportSize(size.width, size.height); setViewportSize(size.width, size.height);
} }
void Scene::renderScene(Renderer &renderer) { void Scene::renderScene(Renderer& renderer) {
if (!isVisible()) if (!isVisible()) {
return; return;
}
// Begin frame with background color
renderer.beginFrame(backgroundColor_); renderer.beginFrame(backgroundColor_);
renderContent(renderer); renderContent(renderer);
renderer.endFrame(); renderer.endFrame();
} }
void Scene::renderContent(Renderer &renderer) { void Scene::renderContent(Renderer& renderer) {
if (!isVisible()) if (!isVisible()) {
return; return;
}
// 在渲染前批量更新所有节点的世界变换
batchUpdateTransforms(); batchUpdateTransforms();
Camera *activeCam = getActiveCamera(); Camera* activeCam = getActiveCamera();
if (activeCam) { if (activeCam) {
renderer.setViewProjection(activeCam->getViewProjectionMatrix()); renderer.setViewProjection(activeCam->getViewProjectionMatrix());
} }
@ -56,7 +59,6 @@ void Scene::updateScene(float dt) {
void Scene::onEnter() { void Scene::onEnter() {
Node::onEnter(); Node::onEnter();
// 初始化空间索引世界边界
if (spatialIndexingEnabled_) { if (spatialIndexingEnabled_) {
spatialManager_.setWorldBounds( spatialManager_.setWorldBounds(
Rect(0, 0, viewportSize_.width, viewportSize_.height)); Rect(0, 0, viewportSize_.width, viewportSize_.height));
@ -64,29 +66,25 @@ void Scene::onEnter() {
} }
void Scene::onExit() { void Scene::onExit() {
// 清理空间索引
spatialManager_.clear(); spatialManager_.clear();
Node::onExit(); Node::onExit();
} }
void Scene::updateNodeInSpatialIndex(Node *node, const Rect &oldBounds, void Scene::updateNodeInSpatialIndex(Node* node, const Rect& oldBounds, const Rect& newBounds) {
const Rect &newBounds) {
if (!spatialIndexingEnabled_ || !node || !node->isSpatialIndexed()) { if (!spatialIndexingEnabled_ || !node || !node->isSpatialIndexed()) {
return; return;
} }
// 如果旧边界有效,先移除
if (!oldBounds.empty()) { if (!oldBounds.empty()) {
spatialManager_.remove(node); spatialManager_.remove(node);
} }
// 如果新边界有效,插入
if (!newBounds.empty()) { if (!newBounds.empty()) {
spatialManager_.insert(node, newBounds); spatialManager_.insert(node, newBounds);
} }
} }
void Scene::removeNodeFromSpatialIndex(Node *node) { void Scene::removeNodeFromSpatialIndex(Node* node) {
if (!spatialIndexingEnabled_ || !node) { if (!spatialIndexingEnabled_ || !node) {
return; return;
} }
@ -94,36 +92,29 @@ void Scene::removeNodeFromSpatialIndex(Node *node) {
spatialManager_.remove(node); spatialManager_.remove(node);
} }
std::vector<Node *> Scene::queryNodesInArea(const Rect &area) const { std::vector<Node*> Scene::queryNodesInArea(const Rect& area) const {
if (!spatialIndexingEnabled_) { if (!spatialIndexingEnabled_) {
return {}; return {};
} }
return spatialManager_.query(area); return spatialManager_.query(area);
} }
std::vector<Node *> Scene::queryNodesAtPoint(const Vec2 &point) const { std::vector<Node*> Scene::queryNodesAtPoint(const Vec2& point) const {
if (!spatialIndexingEnabled_) { if (!spatialIndexingEnabled_) {
return {}; return {};
} }
return spatialManager_.query(point); return spatialManager_.query(point);
} }
std::vector<std::pair<Node *, Node *>> Scene::queryCollisions() const { std::vector<std::pair<Node*, Node*>> Scene::queryCollisions() const {
if (!spatialIndexingEnabled_) { if (!spatialIndexingEnabled_) {
return {}; return {};
} }
return spatialManager_.queryCollisions(); return spatialManager_.queryCollisions();
} }
void Scene::collectRenderCommands(std::vector<RenderCommand> &commands, Ref<Scene> Scene::create() {
int parentZOrder) { return ptr::make<Scene>();
if (!isVisible())
return;
// 从场景的子节点开始收集渲染命令
Node::collectRenderCommands(commands, parentZOrder);
} }
Ptr<Scene> Scene::create() { return makePtr<Scene>(); }
} // namespace extra2d } // namespace extra2d

View File

@ -1,32 +1,29 @@
#include <algorithm> #include <extra2d/render/renderer.h>
#include <extra2d/graphics/render_command.h>
#include <extra2d/graphics/renderer.h>
#include <extra2d/scene/scene_manager.h> #include <extra2d/scene/scene_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
SceneManager &SceneManager::getInstance() { SceneManager& SceneManager::getInstance() {
static SceneManager instance; static SceneManager instance;
return instance; return instance;
} }
void SceneManager::runWithScene(Ptr<Scene> scene) { void SceneManager::runWithScene(Ref<Scene> scene) {
if (!scene) { if (!scene) {
return; return;
} }
if (!sceneStack_.empty()) { if (!sceneStack_.empty()) {
E2D_LOG_WARN("SceneManager: runWithScene should only be called once"); E2D_WARN("SceneManager: runWithScene should only be called once");
return; return;
} }
scene->onEnter(); scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene); sceneStack_.push(scene);
} }
void SceneManager::replaceScene(Ptr<Scene> scene) { void SceneManager::replaceScene(Ref<Scene> scene) {
if (!scene) { if (!scene) {
return; return;
} }
@ -36,19 +33,15 @@ void SceneManager::replaceScene(Ptr<Scene> scene) {
return; return;
} }
// Pop current scene
auto oldScene = sceneStack_.top(); auto oldScene = sceneStack_.top();
oldScene->onExit(); oldScene->onExit();
oldScene->onDetachFromScene();
sceneStack_.pop(); sceneStack_.pop();
// Push new scene
scene->onEnter(); scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene); sceneStack_.push(scene);
} }
void SceneManager::enterScene(Ptr<Scene> scene) { void SceneManager::enterScene(Ref<Scene> scene) {
if (!scene) { if (!scene) {
return; return;
} }
@ -60,18 +53,16 @@ void SceneManager::enterScene(Ptr<Scene> scene) {
} }
} }
void SceneManager::pushScene(Ptr<Scene> scene) { void SceneManager::pushScene(Ref<Scene> scene) {
if (!scene) { if (!scene) {
return; return;
} }
// Pause current scene
if (!sceneStack_.empty()) { if (!sceneStack_.empty()) {
sceneStack_.top()->pause(); sceneStack_.top()->pause();
} }
// Push new scene
scene->onEnter(); scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene); sceneStack_.push(scene);
} }
@ -82,10 +73,8 @@ void SceneManager::popScene() {
auto current = sceneStack_.top(); auto current = sceneStack_.top();
current->onExit(); current->onExit();
current->onDetachFromScene();
sceneStack_.pop(); sceneStack_.pop();
// Resume previous scene
if (!sceneStack_.empty()) { if (!sceneStack_.empty()) {
sceneStack_.top()->resume(); sceneStack_.top()->resume();
} }
@ -96,22 +85,18 @@ void SceneManager::popToRootScene() {
return; return;
} }
// Exit all scenes except root
while (sceneStack_.size() > 1) { while (sceneStack_.size() > 1) {
auto scene = sceneStack_.top(); auto scene = sceneStack_.top();
scene->onExit(); scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop(); sceneStack_.pop();
} }
// Resume root
sceneStack_.top()->resume(); sceneStack_.top()->resume();
} }
void SceneManager::popToScene(const std::string &name) { void SceneManager::popToScene(const std::string& name) {
// Find target scene in stack std::stack<Ref<Scene>> tempStack;
std::stack<Ptr<Scene>> tempStack; Ref<Scene> target = nullptr;
Ptr<Scene> target = nullptr;
while (!sceneStack_.empty()) { while (!sceneStack_.empty()) {
auto scene = sceneStack_.top(); auto scene = sceneStack_.top();
@ -120,7 +105,6 @@ void SceneManager::popToScene(const std::string &name) {
break; break;
} }
scene->onExit(); scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop(); sceneStack_.pop();
} }
@ -129,32 +113,30 @@ void SceneManager::popToScene(const std::string &name) {
} }
} }
Ptr<Scene> SceneManager::getCurrentScene() const { Ref<Scene> SceneManager::getCurrentScene() const {
if (sceneStack_.empty()) { if (sceneStack_.empty()) {
return nullptr; return nullptr;
} }
return sceneStack_.top(); return sceneStack_.top();
} }
Ptr<Scene> SceneManager::getPreviousScene() const { Ref<Scene> SceneManager::getPreviousScene() const {
if (sceneStack_.size() < 2) { if (sceneStack_.size() < 2) {
return nullptr; return nullptr;
} }
// Copy stack to access second top
auto tempStack = sceneStack_; auto tempStack = sceneStack_;
tempStack.pop(); tempStack.pop();
return tempStack.top(); return tempStack.top();
} }
Ptr<Scene> SceneManager::getRootScene() const { Ref<Scene> SceneManager::getRootScene() const {
if (sceneStack_.empty()) { if (sceneStack_.empty()) {
return nullptr; return nullptr;
} }
// Copy stack to access bottom
auto tempStack = sceneStack_; auto tempStack = sceneStack_;
Ptr<Scene> root; Ref<Scene> root;
while (!tempStack.empty()) { while (!tempStack.empty()) {
root = tempStack.top(); root = tempStack.top();
tempStack.pop(); tempStack.pop();
@ -162,13 +144,12 @@ Ptr<Scene> SceneManager::getRootScene() const {
return root; return root;
} }
Ptr<Scene> SceneManager::getSceneByName(const std::string &name) const { Ref<Scene> SceneManager::getSceneByName(const std::string& name) const {
auto it = namedScenes_.find(name); auto it = namedScenes_.find(name);
if (it != namedScenes_.end()) { if (it != namedScenes_.end()) {
return it->second; return it->second;
} }
// Search in stack
auto tempStack = sceneStack_; auto tempStack = sceneStack_;
while (!tempStack.empty()) { while (!tempStack.empty()) {
auto scene = tempStack.top(); auto scene = tempStack.top();
@ -181,7 +162,7 @@ Ptr<Scene> SceneManager::getSceneByName(const std::string &name) const {
return nullptr; return nullptr;
} }
bool SceneManager::hasScene(const std::string &name) const { bool SceneManager::hasScene(const std::string& name) const {
return getSceneByName(name) != nullptr; return getSceneByName(name) != nullptr;
} }
@ -191,43 +172,37 @@ void SceneManager::update(float dt) {
} }
} }
void SceneManager::render(Renderer &renderer) { void SceneManager::render(Renderer& renderer) {
Color clearColor = Colors::Black; Color clearColor = Colors::Black;
if (!sceneStack_.empty()) { if (!sceneStack_.empty()) {
clearColor = sceneStack_.top()->getBackgroundColor(); clearColor = sceneStack_.top()->getBackgroundColor();
} }
E2D_LOG_TRACE("SceneManager::render - beginFrame with color({}, {}, {})", E2D_TRACE("SceneManager::render - beginFrame");
clearColor.r, clearColor.g, clearColor.b);
renderer.beginFrame(clearColor); renderer.beginFrame(clearColor);
if (!sceneStack_.empty()) { if (!sceneStack_.empty()) {
E2D_LOG_TRACE("SceneManager::render - rendering scene content"); E2D_TRACE("SceneManager::render - rendering scene content");
sceneStack_.top()->renderContent(renderer); sceneStack_.top()->renderContent(renderer);
} else { } else {
E2D_LOG_WARN("SceneManager::render - no scene to render"); E2D_WARN("SceneManager::render - no scene to render");
} }
renderer.endFrame(); renderer.endFrame();
E2D_LOG_TRACE("SceneManager::render - endFrame"); E2D_TRACE("SceneManager::render - endFrame");
}
void SceneManager::collectRenderCommands(std::vector<RenderCommand> &commands) {
if (!sceneStack_.empty()) {
sceneStack_.top()->collectRenderCommands(commands, 0);
}
} }
void SceneManager::end() { void SceneManager::end() {
while (!sceneStack_.empty()) { while (!sceneStack_.empty()) {
auto scene = sceneStack_.top(); auto scene = sceneStack_.top();
scene->onExit(); scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop(); sceneStack_.pop();
} }
namedScenes_.clear(); namedScenes_.clear();
} }
void SceneManager::purgeCachedScenes() { namedScenes_.clear(); } void SceneManager::purgeCachedScenes() {
namedScenes_.clear();
}
} // namespace extra2d } // namespace extra2d

View File

@ -1,7 +1,6 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <extra2d/graphics/renderer.h> #include <extra2d/render/renderer.h>
#include <extra2d/graphics/render_command.h>
#include <extra2d/scene/shape_node.h> #include <extra2d/scene/shape_node.h>
#include <limits> #include <limits>
@ -9,19 +8,21 @@ namespace extra2d {
ShapeNode::ShapeNode() = default; ShapeNode::ShapeNode() = default;
Ptr<ShapeNode> ShapeNode::create() { return makePtr<ShapeNode>(); } Ref<ShapeNode> ShapeNode::create() {
return ptr::make<ShapeNode>();
}
Ptr<ShapeNode> ShapeNode::createPoint(const Vec2 &pos, const Color &color) { Ref<ShapeNode> ShapeNode::createPoint(const Vec2& pos, const Color& color) {
auto node = makePtr<ShapeNode>(); auto node = ptr::make<ShapeNode>();
node->shapeType_ = ShapeType::Point; node->shapeType_ = ShapeType::Point;
node->color_ = color; node->color_ = color;
node->points_ = {pos}; node->points_ = {pos};
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createLine(const Vec2 &start, const Vec2 &end, Ref<ShapeNode> ShapeNode::createLine(const Vec2& start, const Vec2& end,
const Color &color, float width) { const Color& color, float width) {
auto node = makePtr<ShapeNode>(); auto node = ptr::make<ShapeNode>();
node->shapeType_ = ShapeType::Line; node->shapeType_ = ShapeType::Line;
node->color_ = color; node->color_ = color;
node->lineWidth_ = width; node->lineWidth_ = width;
@ -29,52 +30,50 @@ Ptr<ShapeNode> ShapeNode::createLine(const Vec2 &start, const Vec2 &end,
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createRect(const Rect &rect, const Color &color, Ref<ShapeNode> ShapeNode::createRect(const Rect& rect, const Color& color, float width) {
float width) { auto node = ptr::make<ShapeNode>();
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Rect; node->shapeType_ = ShapeType::Rect;
node->color_ = color; node->color_ = color;
node->lineWidth_ = width; node->lineWidth_ = width;
node->filled_ = false; node->filled_ = false;
node->points_ = { node->points_ = {
Vec2(rect.left(), rect.top()), Vec2(rect.right(), rect.top()), Vec2(rect.left(), rect.top()),
Vec2(rect.right(), rect.bottom()), Vec2(rect.left(), rect.bottom())}; Vec2(rect.right(), rect.top()),
Vec2(rect.right(), rect.bottom()),
Vec2(rect.left(), rect.bottom())
};
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createFilledRect(const Rect &rect, Ref<ShapeNode> ShapeNode::createFilledRect(const Rect& rect, const Color& color) {
const Color &color) {
auto node = createRect(rect, color, 0); auto node = createRect(rect, color, 0);
node->filled_ = true; node->filled_ = true;
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createCircle(const Vec2 &center, float radius, Ref<ShapeNode> ShapeNode::createCircle(const Vec2& center, float radius,
const Color &color, int segments, const Color& color, int segments, float width) {
float width) { auto node = ptr::make<ShapeNode>();
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Circle; node->shapeType_ = ShapeType::Circle;
node->color_ = color; node->color_ = color;
node->lineWidth_ = width; node->lineWidth_ = width;
node->segments_ = segments; node->segments_ = segments;
node->filled_ = false; node->filled_ = false;
node->points_ = {center}; node->points_ = {center};
// Store radius in a point for simplicity
node->addPoint(Vec2(radius, 0)); node->addPoint(Vec2(radius, 0));
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createFilledCircle(const Vec2 &center, float radius, Ref<ShapeNode> ShapeNode::createFilledCircle(const Vec2& center, float radius,
const Color &color, int segments) { const Color& color, int segments) {
auto node = createCircle(center, radius, color, segments, 0); auto node = createCircle(center, radius, color, segments, 0);
node->filled_ = true; node->filled_ = true;
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createTriangle(const Vec2 &p1, const Vec2 &p2, Ref<ShapeNode> ShapeNode::createTriangle(const Vec2& p1, const Vec2& p2,
const Vec2 &p3, const Color &color, const Vec2& p3, const Color& color, float width) {
float width) { auto node = ptr::make<ShapeNode>();
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Triangle; node->shapeType_ = ShapeType::Triangle;
node->color_ = color; node->color_ = color;
node->lineWidth_ = width; node->lineWidth_ = width;
@ -83,17 +82,16 @@ Ptr<ShapeNode> ShapeNode::createTriangle(const Vec2 &p1, const Vec2 &p2,
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createFilledTriangle(const Vec2 &p1, const Vec2 &p2, Ref<ShapeNode> ShapeNode::createFilledTriangle(const Vec2& p1, const Vec2& p2,
const Vec2 &p3, const Vec2& p3, const Color& color) {
const Color &color) {
auto node = createTriangle(p1, p2, p3, color, 0); auto node = createTriangle(p1, p2, p3, color, 0);
node->filled_ = true; node->filled_ = true;
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createPolygon(const std::vector<Vec2> &points, Ref<ShapeNode> ShapeNode::createPolygon(const std::vector<Vec2>& points,
const Color &color, float width) { const Color& color, float width) {
auto node = makePtr<ShapeNode>(); auto node = ptr::make<ShapeNode>();
node->shapeType_ = ShapeType::Polygon; node->shapeType_ = ShapeType::Polygon;
node->color_ = color; node->color_ = color;
node->lineWidth_ = width; node->lineWidth_ = width;
@ -102,19 +100,19 @@ Ptr<ShapeNode> ShapeNode::createPolygon(const std::vector<Vec2> &points,
return node; return node;
} }
Ptr<ShapeNode> ShapeNode::createFilledPolygon(const std::vector<Vec2> &points, Ref<ShapeNode> ShapeNode::createFilledPolygon(const std::vector<Vec2>& points,
const Color &color) { const Color& color) {
auto node = createPolygon(points, color, 0); auto node = createPolygon(points, color, 0);
node->filled_ = true; node->filled_ = true;
return node; return node;
} }
void ShapeNode::setPoints(const std::vector<Vec2> &points) { void ShapeNode::setPoints(const std::vector<Vec2>& points) {
points_ = points; points_ = points;
updateSpatialIndex(); updateSpatialIndex();
} }
void ShapeNode::addPoint(const Vec2 &point) { void ShapeNode::addPoint(const Vec2& point) {
points_.push_back(point); points_.push_back(point);
updateSpatialIndex(); updateSpatialIndex();
} }
@ -134,8 +132,7 @@ Rect ShapeNode::getBoundingBox() const {
if (shapeType_ == ShapeType::Circle && points_.size() >= 2) { if (shapeType_ == ShapeType::Circle && points_.size() >= 2) {
float radius = std::abs(points_[1].x); float radius = std::abs(points_[1].x);
Vec2 center = points_[0] + offset; Vec2 center = points_[0] + offset;
return Rect(center.x - radius, center.y - radius, radius * 2.0f, return Rect(center.x - radius, center.y - radius, radius * 2.0f, radius * 2.0f);
radius * 2.0f);
} }
float minX = std::numeric_limits<float>::infinity(); float minX = std::numeric_limits<float>::infinity();
@ -143,7 +140,7 @@ Rect ShapeNode::getBoundingBox() const {
float maxX = -std::numeric_limits<float>::infinity(); float maxX = -std::numeric_limits<float>::infinity();
float maxY = -std::numeric_limits<float>::infinity(); float maxY = -std::numeric_limits<float>::infinity();
for (const auto &p : points_) { for (const auto& p : points_) {
Vec2 world = p + offset; Vec2 world = p + offset;
minX = std::min(minX, world.x); minX = std::min(minX, world.x);
minY = std::min(minY, world.y); minY = std::min(minY, world.y);
@ -152,8 +149,7 @@ Rect ShapeNode::getBoundingBox() const {
} }
float inflate = 0.0f; float inflate = 0.0f;
if (!filled_ && if (!filled_ && (shapeType_ == ShapeType::Line || shapeType_ == ShapeType::Rect ||
(shapeType_ == ShapeType::Line || shapeType_ == ShapeType::Rect ||
shapeType_ == ShapeType::Triangle || shapeType_ == ShapeType::Polygon || shapeType_ == ShapeType::Triangle || shapeType_ == ShapeType::Polygon ||
shapeType_ == ShapeType::Point)) { shapeType_ == ShapeType::Point)) {
inflate = std::max(0.0f, lineWidth_ * 0.5f); inflate = std::max(0.0f, lineWidth_ * 0.5f);
@ -166,7 +162,7 @@ Rect ShapeNode::getBoundingBox() const {
(maxY - minY) + inflate * 2.0f); (maxY - minY) + inflate * 2.0f);
} }
void ShapeNode::onDraw(Renderer &renderer) { void ShapeNode::onDraw(Renderer& renderer) {
if (points_.empty()) { if (points_.empty()) {
return; return;
} }
@ -182,23 +178,19 @@ void ShapeNode::onDraw(Renderer &renderer) {
case ShapeType::Line: case ShapeType::Line:
if (points_.size() >= 2) { if (points_.size() >= 2) {
renderer.drawLine(points_[0] + offset, points_[1] + offset, color_, renderer.drawLine(points_[0] + offset, points_[1] + offset, color_, lineWidth_);
lineWidth_);
} }
break; break;
case ShapeType::Rect: case ShapeType::Rect:
if (points_.size() >= 4) { if (points_.size() >= 4) {
if (filled_) {
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x, Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
points_[2].y - points_[0].y); points_[2].y - points_[0].y);
renderer.fillRect(Rect(rect.origin + offset, rect.size), color_); Rect worldRect(rect.origin + offset, rect.size);
if (filled_) {
renderer.fillRect(worldRect, color_);
} else { } else {
for (size_t i = 0; i < points_.size(); ++i) { renderer.drawRect(worldRect, color_, lineWidth_);
Vec2 start = points_[i] + offset;
Vec2 end = points_[(i + 1) % points_.size()] + offset;
renderer.drawLine(start, end, color_, lineWidth_);
}
} }
} }
break; break;
@ -209,8 +201,7 @@ void ShapeNode::onDraw(Renderer &renderer) {
if (filled_) { if (filled_) {
renderer.fillCircle(points_[0] + offset, radius, color_, segments_); renderer.fillCircle(points_[0] + offset, radius, color_, segments_);
} else { } else {
renderer.drawCircle(points_[0] + offset, radius, color_, segments_, renderer.drawCircle(points_[0] + offset, radius, color_, segments_, lineWidth_);
lineWidth_);
} }
} }
break; break;
@ -223,9 +214,7 @@ void ShapeNode::onDraw(Renderer &renderer) {
if (filled_) { if (filled_) {
renderer.fillTriangle(p1, p2, p3, color_); renderer.fillTriangle(p1, p2, p3, color_);
} else { } else {
renderer.drawLine(p1, p2, color_, lineWidth_); renderer.drawTriangle(p1, p2, p3, color_, lineWidth_);
renderer.drawLine(p2, p3, color_, lineWidth_);
renderer.drawLine(p3, p1, color_, lineWidth_);
} }
} }
break; break;
@ -234,7 +223,7 @@ void ShapeNode::onDraw(Renderer &renderer) {
if (!points_.empty()) { if (!points_.empty()) {
std::vector<Vec2> transformedPoints; std::vector<Vec2> transformedPoints;
transformedPoints.reserve(points_.size()); transformedPoints.reserve(points_.size());
for (const auto &p : points_) { for (const auto& p : points_) {
transformedPoints.push_back(p + offset); transformedPoints.push_back(p + offset);
} }
@ -248,101 +237,4 @@ void ShapeNode::onDraw(Renderer &renderer) {
} }
} }
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 } // namespace extra2d

View File

@ -1,44 +1,53 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <extra2d/graphics/renderer.h> #include <extra2d/render/renderer.h>
#include <extra2d/graphics/render_command.h> #include <extra2d/render/texture.h>
#include <extra2d/graphics/texture.h>
#include <extra2d/scene/sprite.h> #include <extra2d/scene/sprite.h>
namespace extra2d { namespace extra2d {
Sprite::Sprite() = default; Sprite::Sprite() = default;
Sprite::Sprite(Ptr<Texture> texture) { setTexture(texture); } Sprite::Sprite(Ref<Texture> texture) {
setTexture(texture);
}
void Sprite::setTexture(Ptr<Texture> texture) { void Sprite::setTexture(Ref<Texture> texture) {
texture_ = texture; texture_ = texture;
if (texture_) { if (texture_) {
textureRect_ = Rect(0, 0, static_cast<float>(texture_->getWidth()), textureRect_ = Rect(0, 0, static_cast<float>(texture_->width()),
static_cast<float>(texture_->getHeight())); static_cast<float>(texture_->height()));
} }
updateSpatialIndex(); updateSpatialIndex();
} }
void Sprite::setTextureRect(const Rect &rect) { void Sprite::setTextureRect(const Rect& rect) {
textureRect_ = rect; textureRect_ = rect;
updateSpatialIndex(); updateSpatialIndex();
} }
void Sprite::setColor(const Color &color) { color_ = color; } void Sprite::setColor(const Color& color) {
color_ = color;
void Sprite::setFlipX(bool flip) { flipX_ = flip; }
void Sprite::setFlipY(bool flip) { flipY_ = flip; }
Ptr<Sprite> Sprite::create() { return makePtr<Sprite>(); }
Ptr<Sprite> Sprite::create(Ptr<Texture> texture) {
return makePtr<Sprite>(texture);
} }
Ptr<Sprite> Sprite::create(Ptr<Texture> texture, const Rect &rect) { void Sprite::setFlipX(bool flip) {
auto sprite = makePtr<Sprite>(texture); flipX_ = flip;
}
void Sprite::setFlipY(bool flip) {
flipY_ = flip;
}
Ref<Sprite> Sprite::create() {
return ptr::make<Sprite>();
}
Ref<Sprite> Sprite::create(Ref<Texture> texture) {
return ptr::make<Sprite>(texture);
}
Ref<Sprite> Sprite::create(Ref<Texture> texture, const Rect& rect) {
auto sprite = ptr::make<Sprite>(texture);
sprite->setTextureRect(rect); sprite->setTextureRect(rect);
return sprite; return sprite;
} }
@ -67,34 +76,26 @@ Rect Sprite::getBoundingBox() const {
return Rect(l, t, std::abs(w), std::abs(h)); return Rect(l, t, std::abs(w), std::abs(h));
} }
void Sprite::onDraw(Renderer &renderer) { void Sprite::onDraw(Renderer& renderer) {
if (!texture_ || !texture_->isValid()) { if (!texture_ || !texture_->isValid()) {
return; return;
} }
// Calculate destination rectangle based on texture rect
float width = textureRect_.width(); float width = textureRect_.width();
float height = textureRect_.height(); float height = textureRect_.height();
// 使用世界变换来获取最终的位置
auto worldTransform = getWorldTransform(); auto worldTransform = getWorldTransform();
// 从世界变换矩阵中提取位置(第四列)
float worldX = worldTransform[3][0]; float worldX = worldTransform[3][0];
float worldY = worldTransform[3][1]; float worldY = worldTransform[3][1];
// 从世界变换矩阵中提取缩放 float worldScaleX = glm::length(glm::vec2(worldTransform[0][0], worldTransform[0][1]));
float worldScaleX = float worldScaleY = glm::length(glm::vec2(worldTransform[1][0], worldTransform[1][1]));
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(); auto anchor = getAnchor();
// 锚点由 Renderer 在绘制时处理,这里只传递位置和尺寸
Rect destRect(worldX, worldY, width * worldScaleX, height * worldScaleY); Rect destRect(worldX, worldY, width * worldScaleX, height * worldScaleY);
// Adjust source rect for flipping
Rect srcRect = textureRect_; Rect srcRect = textureRect_;
if (flipX_) { if (flipX_) {
srcRect.origin.x = srcRect.right(); srcRect.origin.x = srcRect.right();
@ -105,63 +106,9 @@ void Sprite::onDraw(Renderer &renderer) {
srcRect.size.height = -srcRect.size.height; srcRect.size.height = -srcRect.size.height;
} }
// 从世界变换矩阵中提取旋转角度
float worldRotation = std::atan2(worldTransform[0][1], worldTransform[0][0]); float worldRotation = std::atan2(worldTransform[0][1], worldTransform[0][0]);
renderer.drawSprite(*texture_, destRect, srcRect, color_, worldRotation, renderer.drawSprite(*texture_, destRect, srcRect, color_, worldRotation, anchor);
anchor);
}
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 } // namespace extra2d

View File

@ -1,46 +1,89 @@
#include <chrono>
#include <cstdarg> #include <cstdarg>
#include <cstdio> #include <cstdio>
#include <extra2d/services/logger_service.h> #include <extra2d/services/logger_service.h>
#include <mutex> #include <mutex>
#include <SDL.h>
#ifdef _WIN32
#include <windows.h>
#endif
namespace extra2d { namespace extra2d {
#ifdef _WIN32
static bool enableWindowsConsoleFeatures() {
bool success = true;
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut != INVALID_HANDLE_VALUE) {
DWORD dwMode = 0;
if (GetConsoleMode(hOut, &dwMode)) {
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(hOut, dwMode)) {
success = false;
}
} else {
success = false;
}
}
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
return success;
}
static bool g_windowsConsoleInitialized = false;
#endif
class ConsoleLogger::Impl { class ConsoleLogger::Impl {
public: public:
std::mutex mutex_; std::mutex mutex_;
SDL_LogPriority sdlPriorities_[7];
bool customOutputSet_ = false;
}; };
static SDL_LogPriority toSDLPriority(LogLevel lvl) {
switch (lvl) {
case LogLevel::Trace: return SDL_LOG_PRIORITY_VERBOSE;
case LogLevel::Debug: return SDL_LOG_PRIORITY_DEBUG;
case LogLevel::Info: return SDL_LOG_PRIORITY_INFO;
case LogLevel::Registry: return SDL_LOG_PRIORITY_INFO;
case LogLevel::Warn: return SDL_LOG_PRIORITY_WARN;
case LogLevel::Error: return SDL_LOG_PRIORITY_ERROR;
case LogLevel::Fatal: return SDL_LOG_PRIORITY_CRITICAL;
default: return SDL_LOG_PRIORITY_INFO;
}
}
static LogLevel fromSDLPriority(SDL_LogPriority priority) {
switch (priority) {
case SDL_LOG_PRIORITY_VERBOSE: return LogLevel::Trace;
case SDL_LOG_PRIORITY_DEBUG: return LogLevel::Debug;
case SDL_LOG_PRIORITY_INFO: return LogLevel::Info;
case SDL_LOG_PRIORITY_WARN: return LogLevel::Warn;
case SDL_LOG_PRIORITY_ERROR: return LogLevel::Error;
case SDL_LOG_PRIORITY_CRITICAL: return LogLevel::Fatal;
default: return LogLevel::Info;
}
}
static ConsoleLogger* g_loggerInstance = nullptr;
static void SDLCALL sdlLogOutputFunction(void* userdata, int category,
SDL_LogPriority priority,
const char* message) {
if (!g_loggerInstance) return;
ConsoleLogger* logger = static_cast<ConsoleLogger*>(g_loggerInstance);
LogLevel lvl = fromSDLPriority(priority);
if (!logger->enabled(lvl)) return;
const char* levelStr = logger->levelString(lvl);
const char* categoryStr = "";
switch (category) {
case SDL_LOG_CATEGORY_APPLICATION: categoryStr = "APP"; break;
case SDL_LOG_CATEGORY_ERROR: categoryStr = "ERR"; break;
case SDL_LOG_CATEGORY_ASSERT: categoryStr = "ASR"; break;
case SDL_LOG_CATEGORY_SYSTEM: categoryStr = "SYS"; break;
case SDL_LOG_CATEGORY_AUDIO: categoryStr = "AUD"; break;
case SDL_LOG_CATEGORY_VIDEO: categoryStr = "VID"; break;
case SDL_LOG_CATEGORY_RENDER: categoryStr = "RDR"; break;
case SDL_LOG_CATEGORY_INPUT: categoryStr = "INP"; break;
case SDL_LOG_CATEGORY_TEST: categoryStr = "TST"; break;
default: categoryStr = "GEN"; break;
}
Uint32 ticks = SDL_GetTicks();
Uint32 seconds = ticks / 1000;
Uint32 minutes = seconds / 60;
Uint32 hours = minutes / 60;
if (logger->colors()) {
std::string color = logger->ansiColor(lvl);
const char* reset = "\033[0m";
printf("%s[%02u:%02u:%02u.%03u] [%s][%s] %s%s\n",
color.c_str(),
hours % 24, minutes % 60, seconds % 60, ticks % 1000,
levelStr, categoryStr, message, reset);
} else {
printf("[%02u:%02u:%02u.%03u] [%s][%s] %s\n",
hours % 24, minutes % 60, seconds % 60, ticks % 1000,
levelStr, categoryStr, message);
}
}
ConsoleLogger::ConsoleLogger() ConsoleLogger::ConsoleLogger()
: level_(LogLevel::Info), colors_(true), : level_(LogLevel::Info), colors_(true),
impl_(std::make_unique<Impl>()) { impl_(std::make_unique<Impl>()) {
@ -55,124 +98,155 @@ ConsoleLogger::ConsoleLogger()
levelColors_[static_cast<int>(LogLevel::Error)] = LogColor::Red(); levelColors_[static_cast<int>(LogLevel::Error)] = LogColor::Red();
levelColors_[static_cast<int>(LogLevel::Fatal)] = LogColor::Magenta(); levelColors_[static_cast<int>(LogLevel::Fatal)] = LogColor::Magenta();
#ifdef _WIN32 for (int i = 0; i < 7; ++i) {
if (!g_windowsConsoleInitialized) { impl_->sdlPriorities_[i] = toSDLPriority(static_cast<LogLevel>(i));
g_windowsConsoleInitialized = enableWindowsConsoleFeatures();
} }
#endif
} }
ConsoleLogger::~ConsoleLogger() = default; ConsoleLogger::~ConsoleLogger() {
if (impl_->customOutputSet_) {
SDL_LogSetOutputFunction(nullptr, nullptr);
}
g_loggerInstance = nullptr;
}
bool ConsoleLogger::init() { bool ConsoleLogger::init() {
g_loggerInstance = this;
SDL_LogSetOutputFunction(sdlLogOutputFunction, this);
impl_->customOutputSet_ = true;
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, toSDLPriority(level_));
setState(ServiceState::Running); setState(ServiceState::Running);
return true; return true;
} }
void ConsoleLogger::shutdown() { setState(ServiceState::Stopped); } void ConsoleLogger::shutdown() {
if (impl_->customOutputSet_) {
SDL_LogSetOutputFunction(nullptr, nullptr);
impl_->customOutputSet_ = false;
}
g_loggerInstance = nullptr;
setState(ServiceState::Stopped);
}
void ConsoleLogger::level(LogLevel lvl) { level_ = lvl; } void ConsoleLogger::level(LogLevel lvl) {
level_ = lvl;
SDL_LogPriority priority = toSDLPriority(lvl);
SDL_LogSetAllPriority(priority);
}
LogLevel ConsoleLogger::level() const { return level_; } LogLevel ConsoleLogger::level() const {
return level_;
}
bool ConsoleLogger::enabled(LogLevel lvl) const { bool ConsoleLogger::enabled(LogLevel lvl) const {
return static_cast<int>(lvl) >= static_cast<int>(level_); return static_cast<int>(lvl) >= static_cast<int>(level_);
} }
void ConsoleLogger::log(LogLevel lvl, const char *fmt, ...) { void ConsoleLogger::log(LogLevel lvl, const char *fmt, ...) {
if (!enabled(lvl)) if (!enabled(lvl)) return;
return;
char buffer[1024]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
output(lvl, buffer); SDL_LogPriority priority = toSDLPriority(lvl);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", buffer);
} }
void ConsoleLogger::log(LogLevel lvl, const std::string &msg) { void ConsoleLogger::log(LogLevel lvl, const std::string &msg) {
if (!enabled(lvl)) if (!enabled(lvl)) return;
return;
output(lvl, msg.c_str()); SDL_LogPriority priority = toSDLPriority(lvl);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", msg.c_str());
} }
void ConsoleLogger::trace(const char *fmt, ...) { void ConsoleLogger::trace(const char *fmt, ...) {
if (!enabled(LogLevel::Trace)) if (!enabled(LogLevel::Trace)) return;
return;
char buffer[1024]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
output(LogLevel::Trace, buffer);
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::debug(const char *fmt, ...) { void ConsoleLogger::debug(const char *fmt, ...) {
if (!enabled(LogLevel::Debug)) if (!enabled(LogLevel::Debug)) return;
return;
char buffer[1024]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
output(LogLevel::Debug, buffer);
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::info(const char *fmt, ...) { void ConsoleLogger::info(const char *fmt, ...) {
if (!enabled(LogLevel::Info)) if (!enabled(LogLevel::Info)) return;
return;
char buffer[1024]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
output(LogLevel::Info, buffer);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::registry(const char *fmt, ...) { void ConsoleLogger::registry(const char *fmt, ...) {
if (!enabled(LogLevel::Registry)) if (!enabled(LogLevel::Registry)) return;
return;
char buffer[1024]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
output(LogLevel::Registry, buffer);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[REGISTRY] %s", buffer);
} }
void ConsoleLogger::warn(const char *fmt, ...) { void ConsoleLogger::warn(const char *fmt, ...) {
if (!enabled(LogLevel::Warn)) if (!enabled(LogLevel::Warn)) return;
return;
char buffer[1024]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
output(LogLevel::Warn, buffer);
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::error(const char *fmt, ...) { void ConsoleLogger::error(const char *fmt, ...) {
if (!enabled(LogLevel::Error)) if (!enabled(LogLevel::Error)) return;
return;
char buffer[1024]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
output(LogLevel::Error, buffer);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::fatal(const char *fmt, ...) { void ConsoleLogger::fatal(const char *fmt, ...) {
if (!enabled(LogLevel::Fatal)) if (!enabled(LogLevel::Fatal)) return;
return;
char buffer[1024]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
output(LogLevel::Fatal, buffer);
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::levelColor(LogLevel lvl, const LogColor &c) { void ConsoleLogger::levelColor(LogLevel lvl, const LogColor &c) {
@ -201,53 +275,16 @@ std::string ConsoleLogger::ansiColor(LogLevel lvl) {
return std::string(buf); return std::string(buf);
} }
void ConsoleLogger::output(LogLevel lvl, const char *msg) {
std::lock_guard<std::mutex> lock(impl_->mutex_);
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) %
1000;
std::tm tm;
#ifdef _WIN32
localtime_s(&tm, &time);
#else
localtime_r(&time, &tm);
#endif
const char *levelStr = levelString(lvl);
const char *reset = "\033[0m";
if (colors_) {
std::string color = ansiColor(lvl);
printf("%s[%02d:%02d:%02d.%03d] [%s] %s%s\n", color.c_str(), tm.tm_hour,
tm.tm_min, tm.tm_sec, (int)ms.count(), levelStr, msg, reset);
} else {
printf("[%02d:%02d:%02d.%03d] [%s] %s\n", tm.tm_hour, tm.tm_min, tm.tm_sec,
(int)ms.count(), levelStr, msg);
}
}
const char *ConsoleLogger::levelString(LogLevel lvl) { const char *ConsoleLogger::levelString(LogLevel lvl) {
switch (lvl) { switch (lvl) {
case LogLevel::Trace: case LogLevel::Trace: return "TRACE";
return "TRACE"; case LogLevel::Debug: return "DEBUG";
case LogLevel::Debug: case LogLevel::Info: return "INFO";
return "DEBUG"; case LogLevel::Registry: return "REGISTRY";
case LogLevel::Info: case LogLevel::Warn: return "WARN";
return "INFO"; case LogLevel::Error: return "ERROR";
case LogLevel::Registry: case LogLevel::Fatal: return "FATAL";
return "REGISTRY"; default: return "UNKNOWN";
case LogLevel::Warn:
return "WARN";
case LogLevel::Error:
return "ERROR";
case LogLevel::Fatal:
return "FATAL";
default:
return "UNKNOWN";
} }
} }

View File

@ -1,5 +1,5 @@
#include <extra2d/utils/data.h> #include <extra2d/utils/data.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <simpleini/SimpleIni.h> #include <simpleini/SimpleIni.h>
// Switch 平台特定头文件 // Switch 平台特定头文件

View File

@ -1,104 +0,0 @@
#include <extra2d/utils/logger.h>
namespace extra2d {
// 静态成员定义
LogLevel Logger::level_ = LogLevel::Info;
bool Logger::initialized_ = false;
bool Logger::consoleOutput_ = true;
bool Logger::fileOutput_ = false;
std::string Logger::logFile_;
/**
* @brief
* @param level
* @return
*/
const char *Logger::getLevelString(LogLevel level) {
switch (level) {
case LogLevel::Trace:
return "TRACE";
case LogLevel::Debug:
return "DEBUG";
case LogLevel::Info:
return "INFO ";
case LogLevel::Warn:
return "WARN ";
case LogLevel::Error:
return "ERROR";
case LogLevel::Fatal:
return "FATAL";
default:
return "UNKNOWN";
}
}
/**
* @brief
*/
void Logger::init() {
if (initialized_) {
return;
}
// 设置 SDL 日志级别为详细模式(允许所有级别的日志)
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE);
initialized_ = true;
log(LogLevel::Info, "Logger initialized with SDL2");
}
/**
* @brief
*/
void Logger::shutdown() {
if (initialized_) {
log(LogLevel::Info, "Logger shutting down");
}
initialized_ = false;
}
/**
* @brief
* @param level
*/
void Logger::setLevel(LogLevel level) {
level_ = level;
// 同时设置 SDL 的日志级别
if (level != LogLevel::Off) {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level));
}
}
/**
* @brief
* @param enable
*/
void Logger::setConsoleOutput(bool enable) {
consoleOutput_ = enable;
// SDL2 日志默认输出到控制台,通过设置日志优先级控制
if (!enable) {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL);
} else {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level_));
}
}
/**
* @brief
* @param filename
*/
void Logger::setFileOutput(const std::string &filename) {
logFile_ = filename;
fileOutput_ = !filename.empty();
if (fileOutput_) {
// SDL2 使用 SDL_LogSetOutputFunction 可以重定向日志输出
// 这里我们记录文件路径,实际文件输出可以通过自定义回调实现
log(LogLevel::Info, "File output configured: {}", filename);
}
}
} // namespace extra2d

View File

@ -1,9 +0,0 @@
#include <extra2d/utils/object_pool.h>
namespace extra2d {
// ObjectPoolManager 单例实现
// 所有对象池通过静态局部变量自动管理生命周期
// 程序退出时自动清理,无需手动调用 cleanup
} // namespace extra2d

View File

@ -1,61 +1,31 @@
#include <extra2d/services/logger_service.h>
#include <extra2d/window/window.h> #include <extra2d/window/window.h>
#include <extra2d/platform/input.h>
#include <extra2d/utils/logger.h>
#include <SDL.h> #include <SDL.h>
#include <glad/glad.h>
namespace extra2d { namespace extra2d {
Window::Window() Window::Window()
: sdlWindow_(nullptr), glContext_(nullptr), : sdlWindow_(nullptr), width_(1280), height_(720), fullscreen_(true),
width_(1280), height_(720), vsync_(true), shouldClose_(false), shouldClose_(false) {}
fullscreen_(true) {
}
Window::~Window() { destroy(); } Window::~Window() { destroy(); }
bool Window::create(const WindowConfig &config) { bool Window::create(const WindowConfig &config) {
if (sdlWindow_ != nullptr) { if (sdlWindow_ != nullptr) {
E2D_LOG_WARN("Window already created"); E2D_WARN("Window already created");
return false; return false;
} }
width_ = config.width; width_ = config.width;
height_ = config.height; height_ = config.height;
vsync_ = config.vsync;
fullscreen_ = config.fullscreen; fullscreen_ = config.fullscreen;
if (!initSDL(config)) {
E2D_LOG_ERROR("Failed to initialize SDL2");
return false;
}
input_ = makeUnique<Input>();
input_->init();
E2D_LOG_INFO("Window created: {}x{}", width_, height_);
return true;
}
bool Window::initSDL(const WindowConfig &config) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) {
E2D_LOG_ERROR("SDL_Init failed: {}", SDL_GetError()); E2D_ERROR("SDL_Init failed: {}", SDL_GetError());
return false; return false;
} }
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
Uint32 windowFlags = SDL_WINDOW_OPENGL; Uint32 windowFlags = SDL_WINDOW_OPENGL;
if (config.fullscreen) { if (config.fullscreen) {
@ -73,68 +43,21 @@ bool Window::initSDL(const WindowConfig &config) {
width_, height_, windowFlags); width_, height_, windowFlags);
if (!sdlWindow_) { if (!sdlWindow_) {
E2D_LOG_ERROR("SDL_CreateWindow failed: {}", SDL_GetError()); E2D_ERROR("SDL_CreateWindow failed: {}", SDL_GetError());
SDL_Quit(); SDL_Quit();
return false; return false;
} }
glContext_ = SDL_GL_CreateContext(sdlWindow_); E2D_INFO("Window created: {}x{}", width_, height_);
if (!glContext_) {
E2D_LOG_ERROR("SDL_GL_CreateContext failed: {}", SDL_GetError());
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
SDL_Quit();
return false;
}
if (SDL_GL_MakeCurrent(sdlWindow_, glContext_) != 0) {
E2D_LOG_ERROR("SDL_GL_MakeCurrent failed: {}", SDL_GetError());
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
SDL_Quit();
return false;
}
if (gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress)) == 0) {
E2D_LOG_ERROR("gladLoadGLES2Loader failed");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
SDL_Quit();
return false;
}
SDL_GL_SetSwapInterval(vsync_ ? 1 : 0);
E2D_LOG_INFO("SDL2 + OpenGL ES 3.0 initialized successfully");
E2D_LOG_INFO("OpenGL Version: {}", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
E2D_LOG_INFO("OpenGL Renderer: {}", reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
return true; return true;
} }
void Window::deinitSDL() {
if (glContext_) {
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
}
if (sdlWindow_) {
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
}
SDL_Quit();
}
void Window::destroy() { void Window::destroy() {
if (sdlWindow_ != nullptr) { if (sdlWindow_ != nullptr) {
input_.reset(); SDL_DestroyWindow(sdlWindow_);
deinitSDL(); sdlWindow_ = nullptr;
E2D_LOG_INFO("Window destroyed"); SDL_Quit();
E2D_INFO("Window destroyed");
} }
} }
@ -155,16 +78,6 @@ void Window::pollEvents() {
break; break;
} }
} }
if (input_) {
input_->update();
}
}
void Window::swapBuffers() {
if (sdlWindow_) {
SDL_GL_SwapWindow(sdlWindow_);
}
} }
bool Window::shouldClose() const { return shouldClose_; } bool Window::shouldClose() const { return shouldClose_; }
@ -193,9 +106,4 @@ void Window::setFullscreen(bool fullscreen) {
} }
} }
void Window::setVSync(bool enabled) {
vsync_ = enabled;
SDL_GL_SetSwapInterval(enabled ? 1 : 0);
}
} // namespace extra2d } // namespace extra2d

View File

@ -44,7 +44,7 @@ function define_extra2d_engine()
add_cxxflags("-Wno-deprecated-copy", "-Wno-class-memaccess", {force = true}) add_cxxflags("-Wno-deprecated-copy", "-Wno-class-memaccess", {force = true})
if is_mode("debug") then if is_mode("debug") then
add_defines("E2D_DEBUG", "_DEBUG", {public = true}) add_defines("_DEBUG", {public = true})
add_cxxflags("-O0", "-g", {force = true}) add_cxxflags("-O0", "-g", {force = true})
else else
add_defines("NDEBUG", {public = true}) add_defines("NDEBUG", {public = true})