diff --git a/Frostbite2D/include/frostbite2D/core/application.h b/Frostbite2D/include/frostbite2D/core/application.h index a4ce912..976d249 100644 --- a/Frostbite2D/include/frostbite2D/core/application.h +++ b/Frostbite2D/include/frostbite2D/core/application.h @@ -127,12 +127,18 @@ public: * @return 应用配置常量引用 */ const AppConfig& getConfig() const; - + /** * @brief 获取窗口 * @return 窗口指针 */ Window* getWindow() const { return window_; } + + /** + * @brief 获取渲染器 + * @return 渲染器指针 + */ + class Renderer* getRenderer() const { return renderer_; } /** * @brief 获取 delta time @@ -182,6 +188,7 @@ private: std::vector modules_; Window* window_ = nullptr; + class Renderer* renderer_ = nullptr; AppConfig config_; bool initialized_ = false; diff --git a/Frostbite2D/include/frostbite2D/graphics/batch.h b/Frostbite2D/include/frostbite2D/graphics/batch.h new file mode 100644 index 0000000..c4936da --- /dev/null +++ b/Frostbite2D/include/frostbite2D/graphics/batch.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include +#include + +namespace frostbite2D { + +struct BatchKey { + uint32_t shaderID; + uint32_t textureID; + uint32_t blendMode; + + bool operator<(const BatchKey& other) const { + if (shaderID != other.shaderID) return shaderID < other.shaderID; + if (textureID != other.textureID) return textureID < other.textureID; + return blendMode < other.blendMode; + } +}; + +class Batch { +public: + static constexpr int MAX_QUADS = 2048; + static constexpr int MAX_VERTICES = MAX_QUADS * 4; + static constexpr int MAX_INDICES = MAX_QUADS * 6; + + Batch(); + ~Batch(); + + bool init(); + void shutdown(); + + void begin(); + void end(); + + void submitQuad(const Quad& quad, const Transform2D& transform, + Ptr texture, Shader* shader, + BlendMode blendMode = BlendMode::Normal); + + void flush(); + void flushIfNeeded(const BatchKey& newKey, Shader* shader, Ptr texture); + + private: + void setupMesh(); + void flushCurrentBatch(); + + struct BatchData { + std::vector vertices; + std::vector indices; + BatchKey key; + Shader* shader = nullptr; + Ptr texture; + }; + + uint32_t vao_ = 0; + uint32_t vbo_ = 0; + uint32_t ibo_ = 0; + + BatchData currentBatch_; + std::vector batches_; + + bool begun_ = false; +}; + +} \ No newline at end of file diff --git a/Frostbite2D/include/frostbite2D/graphics/camera.h b/Frostbite2D/include/frostbite2D/graphics/camera.h new file mode 100644 index 0000000..70c7484 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/graphics/camera.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace frostbite2D { + +class Camera { +public: + Camera(); + + void setPosition(const Vec2& pos); + void setZoom(float zoom); + void setViewport(int width, int height); + void setFlipY(bool flip) { flipY_ = flip; } // 调试用:Y轴翻转开关 + + const Vec2& getPosition() const { return position_; } + float getZoom() const { return zoom_; } + + void lookAt(const Vec2& target); + void move(const Vec2& delta); + void zoomAt(float factor, const Vec2& screenPos); + + glm::mat4 getViewMatrix() const; + glm::mat4 getProjectionMatrix() const; + + private: + Vec2 position_; + float zoom_ = 1.0f; + int viewportWidth_ = 1280; + int viewportHeight_ = 720; + bool flipY_ = false; // 调试用:Y轴翻转开关 +}; + +} \ No newline at end of file diff --git a/Frostbite2D/include/frostbite2D/graphics/renderer.h b/Frostbite2D/include/frostbite2D/graphics/renderer.h new file mode 100644 index 0000000..e9e572d --- /dev/null +++ b/Frostbite2D/include/frostbite2D/graphics/renderer.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace frostbite2D { + +class Renderer { +public: + static Renderer& get(); + + bool init(); + void shutdown(); + + void beginFrame(); + void endFrame(); + void flush(); + + void setViewport(int x, int y, int width, int height); + void setClearColor(float r, float g, float b, float a = 1.0f); + void setClearColor(const Color& color); + void clear(uint32_t flags); + + void setCamera(Camera* camera); + Camera* getCamera() { return camera_; } + + void drawQuad(const Vec2& pos, const Size& size, float cr = 1.0f, float cg = 1.0f, + float cb = 1.0f, float ca = 1.0f); + void drawQuad(const Vec2& pos, const Size& size, Ptr texture); + void drawQuad(const Rect& rect, const Color& color); + + void drawSprite(const Vec2& pos, const Size& size, Ptr texture); + void drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize, + Ptr texture, const Color& color = Color(1, 1, 1, 1)); + + ShaderManager& getShaderManager() { return shaderManager_; } + Batch& getBatch() { return batch_; } + +private: + Renderer(); + ~Renderer(); + + void setupBlendMode(BlendMode mode); + void updateUniforms(); + + ShaderManager shaderManager_; + Batch batch_; + Camera* camera_ = nullptr; + + uint32_t clearColor_[4] = {0, 0, 0, 255}; + int viewportX_ = 0; + int viewportY_ = 0; + int viewportWidth_ = 1280; + int viewportHeight_ = 720; + + bool initialized_ = false; + + Renderer(const Renderer&) = delete; + Renderer& operator=(const Renderer&) = delete; +}; + +} \ No newline at end of file diff --git a/Frostbite2D/include/frostbite2D/graphics/shader.h b/Frostbite2D/include/frostbite2D/graphics/shader.h new file mode 100644 index 0000000..c8386c7 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/graphics/shader.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +namespace frostbite2D { + +class Shader { +public: + Shader() = default; + ~Shader(); + + void use(); + void unuse(); + + bool isValid() const { return programID_ != 0; } + uint32_t getID() const { return programID_; } + const std::string& getName() const { return name_; } + + void setInt(const std::string& name, int value); + void setFloat(const std::string& name, float value); + void setVec2(const std::string& name, const Vec2& value); + void setVec3(const std::string& name, const glm::vec3& value); + void setVec4(const std::string& name, const glm::vec4& value); + void setMat3(const std::string& name, const glm::mat3& value); + void setMat4(const std::string& name, const glm::mat4& value); + void setTexture(const std::string& name, int slot); + +private: + Shader(const std::string& name, uint32_t programID); + + friend class ShaderManager; + + bool compile(uint32_t type, const std::string& source); + bool link(uint32_t vertexID, uint32_t fragmentID); + int getUniformLocation(const std::string& name); + + uint32_t programID_ = 0; + std::string name_; + std::unordered_map uniformLocations_; + + Shader(const Shader&) = delete; + Shader& operator=(const Shader&) = delete; +}; + +} \ No newline at end of file diff --git a/Frostbite2D/include/frostbite2D/graphics/shader_manager.h b/Frostbite2D/include/frostbite2D/graphics/shader_manager.h new file mode 100644 index 0000000..20305c3 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/graphics/shader_manager.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include + +namespace frostbite2D { + +class Renderer; + +class ShaderManager { +public: + static ShaderManager& get(); + + bool init(const std::string& shadersDir); + void shutdown(); + + Shader* getShader(const std::string& name); + bool hasShader(const std::string& name) const; + + ~ShaderManager(); + + ShaderManager() = default; + +private: + + std::unordered_map> shaders_; + std::string shadersDir_; + + bool loadShadersFromConfig(const std::string& configPath); + bool loadShader(const std::string& name, const std::string& vertPath, + const std::string& fragPath); + + ShaderManager(const ShaderManager&) = delete; + ShaderManager& operator=(const ShaderManager&) = delete; +}; + +} \ No newline at end of file diff --git a/Frostbite2D/include/frostbite2D/graphics/texture.h b/Frostbite2D/include/frostbite2D/graphics/texture.h new file mode 100644 index 0000000..c34a77b --- /dev/null +++ b/Frostbite2D/include/frostbite2D/graphics/texture.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include + +namespace frostbite2D { + +class Texture { +public: + static Ptr loadFromFile(const std::string& path); + static Ptr createFromMemory(uint8* data, int width, int height, int channels); + static Ptr createEmpty(int width, int height); + + ~Texture(); + + void bind(uint32_t slot = 0); + void unbind(); + + void setWrapMode(uint32_t wrapS, uint32_t wrapT); + void setFilterMode(uint32_t minFilter, uint32_t magFilter); + + int getWidth() const { return width_; } + int getHeight() const { return height_; } + uint32_t getID() const { return textureID_; } + const std::string& getPath() const { return path_; } + +private: + Texture(int width, int height, uint32_t id); + + friend class std::shared_ptr; + template friend Ptr makePtr(Args &&...args); + + uint32_t textureID_ = 0; + int width_ = 0; + int height_ = 0; + int channels_ = 0; + std::string path_; + + Texture() = default; + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; +}; + +} \ No newline at end of file diff --git a/Frostbite2D/include/frostbite2D/graphics/types.h b/Frostbite2D/include/frostbite2D/graphics/types.h new file mode 100644 index 0000000..8226651 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/graphics/types.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include + +namespace frostbite2D { + +enum class BlendMode { + None, + Normal, + Additive, + Subtractive, + Multiply +}; + +enum class PrimitiveType { + Triangles, + TriangleStrip, + TriangleFan, + Quads +}; + +struct Vertex { + Vec2 position; + Vec2 texCoord; + float r, g, b, a; + + Vertex() = default; + Vertex(const Vec2& pos, const Vec2& uv, float cr, float cg, float cb, float ca) + : position(pos), texCoord(uv), r(cr), g(cg), b(cb), a(ca) {} +}; + +struct Quad { + Vertex vertices[4]; + + Quad() = default; + + static Quad create(const Rect& rect, float cr = 1.0f, float cg = 1.0f, + float cb = 1.0f, float ca = 1.0f) { + Quad q; + Vec2 bl(rect.left(), rect.bottom()); + Vec2 br(rect.right(), rect.bottom()); + Vec2 tl(rect.left(), rect.top()); + Vec2 tr(rect.right(), rect.top()); + + q.vertices[0] = Vertex(bl, Vec2(0, 0), cr, cg, cb, ca); + q.vertices[1] = Vertex(br, Vec2(1, 0), cr, cg, cb, ca); + q.vertices[2] = Vertex(tl, Vec2(0, 1), cr, cg, cb, ca); + q.vertices[3] = Vertex(tr, Vec2(1, 1), cr, cg, cb, ca); + + return q; + } + + static Quad createTextured(const Rect& destRect, const Rect& srcRect, + const Vec2& texSize, float cr = 1.0f, + float cg = 1.0f, float cb = 1.0f, float ca = 1.0f) { + Quad q; + + Vec2 bl(destRect.left(), destRect.bottom()); + Vec2 br(destRect.right(), destRect.bottom()); + Vec2 tl(destRect.left(), destRect.top()); + Vec2 tr(destRect.right(), destRect.top()); + + Vec2 uvBL(srcRect.left() / texSize.x, srcRect.bottom() / texSize.y); + Vec2 uvBR(srcRect.right() / texSize.x, srcRect.bottom() / texSize.y); + Vec2 uvTL(srcRect.left() / texSize.x, srcRect.top() / texSize.y); + Vec2 uvTR(srcRect.right() / texSize.x, srcRect.top() / texSize.y); + + q.vertices[0] = Vertex(bl, uvBL, cr, cg, cb, ca); + q.vertices[1] = Vertex(br, uvBR, cr, cg, cb, ca); + q.vertices[2] = Vertex(tl, uvTL, cr, cg, cb, ca); + q.vertices[3] = Vertex(tr, uvTR, cr, cg, cb, ca); + + return q; + } +}; + +} \ No newline at end of file diff --git a/Frostbite2D/include/frostbite2D/types/type_color.h b/Frostbite2D/include/frostbite2D/types/type_color.h index db5c917..8df9f8a 100644 --- a/Frostbite2D/include/frostbite2D/types/type_color.h +++ b/Frostbite2D/include/frostbite2D/types/type_color.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include namespace frostbite2D { diff --git a/Frostbite2D/include/frostbite2D/types/type_math.h b/Frostbite2D/include/frostbite2D/types/type_math.h index f74b263..40c2033 100644 --- a/Frostbite2D/include/frostbite2D/types/type_math.h +++ b/Frostbite2D/include/frostbite2D/types/type_math.h @@ -196,7 +196,7 @@ struct Rect { constexpr Rect() = default; constexpr Rect(float x, float y, float w, float h) : origin(x, y), size(w, h) {} - constexpr Rect(const Point &o, const Size &s) : origin(o), size(s) {} + constexpr Rect(const Point& o, const Size& s) : origin(o), size(s) {} float left() const { return origin.x; } float top() const { return origin.y; } diff --git a/Frostbite2D/src/frostbite2D/core/application.cpp b/Frostbite2D/src/frostbite2D/core/application.cpp index cffde98..c2080f5 100644 --- a/Frostbite2D/src/frostbite2D/core/application.cpp +++ b/Frostbite2D/src/frostbite2D/core/application.cpp @@ -1,7 +1,9 @@ #include #include #include +#include #include +#include namespace frostbite2D { Application &Application::get() { @@ -69,6 +71,12 @@ void Application::shutdown() { destroyAllModules(); + // 关闭渲染器 + if (renderer_) { + renderer_->shutdown(); + renderer_ = nullptr; + } + if (window_) { window_->destroy(); window_ = nullptr; @@ -89,6 +97,14 @@ Application::~Application() { bool Application::initCoreModules() { registerCoreServices(); + + // 初始化渲染器 + renderer_ = &Renderer::get(); + if (!renderer_->init()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize renderer"); + return false; + } + return true; } @@ -234,6 +250,28 @@ void Application::render() { } } + // 简单的测试代码 - 绘制彩色方块 + if (renderer_) { + static float angle = 0.0f; + angle += 0.02f; + + renderer_->beginFrame(); + + // 绘制橙色方块 + renderer_->drawQuad(Vec2(0, 0), Size(200, 150), 1.0f, 0.5f, 0.2f); + + // 绘制蓝色移动方块 + float x = 1920.0 / 2 + cosf(angle) * 100.0f; + float y = 1080.0 / 2 + sinf(angle) * 100.0f; + renderer_->drawQuad(Vec2(x, y), Size(100, 100), 0.3f, 0.5f, 1.0f); + + renderer_->drawQuad(Vec2(1920 - 100, 1080 - 150), Size(100, 150), 1.0f, + 0.5f, 0.2f); + + renderer_->endFrame(); + renderer_->flush(); + } + if (window_) { window_->swap(); } diff --git a/Frostbite2D/src/frostbite2D/core/window.cpp b/Frostbite2D/src/frostbite2D/core/window.cpp index 4617c03..da78819 100644 --- a/Frostbite2D/src/frostbite2D/core/window.cpp +++ b/Frostbite2D/src/frostbite2D/core/window.cpp @@ -81,7 +81,20 @@ bool Window::create(const WindowConfig &cfg) { SDL_GL_SetSwapInterval(cfg.vsync ? 1 : 0); - SDL_GetWindowSize(sdlWindow_, &width_, &height_); + // 获取实际的可绘制大小(考虑高 DPI 和 Switch 等平台) + int drawableWidth, drawableHeight; + SDL_GL_GetDrawableSize(sdlWindow_, &drawableWidth, &drawableHeight); + width_ = drawableWidth; + height_ = drawableHeight; + + // 计算缩放比例 + int windowWidth, windowHeight; + SDL_GetWindowSize(sdlWindow_, &windowWidth, &windowHeight); + if (windowWidth > 0 && windowHeight > 0) { + scaleX_ = static_cast(drawableWidth) / static_cast(windowWidth); + scaleY_ = static_cast(drawableHeight) / static_cast(windowHeight); + } + fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; vsync_ = cfg.vsync; @@ -119,7 +132,11 @@ void Window::destroy() { void Window::poll() {} -void Window::swap() {} +void Window::swap() { + if (sdlWindow_ && glContext_) { + SDL_GL_SwapWindow(sdlWindow_); + } +} void Window::close() { shouldClose_ = true; } diff --git a/Frostbite2D/src/frostbite2D/graphics/batch.cpp b/Frostbite2D/src/frostbite2D/graphics/batch.cpp new file mode 100644 index 0000000..776b2cc --- /dev/null +++ b/Frostbite2D/src/frostbite2D/graphics/batch.cpp @@ -0,0 +1,180 @@ +#include "SDL_log.h" +#include +#include +#include + +namespace frostbite2D { + +Batch::Batch() { + currentBatch_.vertices.reserve(MAX_VERTICES); + currentBatch_.indices.reserve(MAX_INDICES); +} + +Batch::~Batch() { + shutdown(); +} + +bool Batch::init() { + glGenVertexArrays(1, &vao_); + glGenBuffers(1, &vbo_); + glGenBuffers(1, &ibo_); + + setupMesh(); + + SDL_Log("Batch system initialized"); + return true; +} + +void Batch::shutdown() { + if (vao_ != 0) { + glDeleteVertexArrays(1, &vao_); + vao_ = 0; + } + if (vbo_ != 0) { + glDeleteBuffers(1, &vbo_); + vbo_ = 0; + } + if (ibo_ != 0) { + glDeleteBuffers(1, &ibo_); + ibo_ = 0; + } + + currentBatch_.vertices.clear(); + currentBatch_.indices.clear(); + batches_.clear(); +} + +void Batch::setupMesh() { + glBindVertexArray(vao_); + + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_INDICES * sizeof(uint16), nullptr, GL_DYNAMIC_DRAW); + + int stride = sizeof(Vertex); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, (void*)0); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(Vertex, texCoord)); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(Vertex, r)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void Batch::begin() { + begun_ = true; + currentBatch_.vertices.clear(); + currentBatch_.indices.clear(); + currentBatch_.key = {0, 0, 0}; + currentBatch_.shader = nullptr; + currentBatch_.texture.reset(); +} + +void Batch::end() { + if (begun_) { + flush(); + begun_ = false; + } +} + +void Batch::submitQuad(const Quad& quad, const Transform2D& transform, + Ptr texture, Shader* shader, + BlendMode blendMode) { + if (!begun_) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Batch not started, call begin() first"); + return; + } + + uint32_t textureID = texture ? texture->getID() : 0; + uint32_t shaderID = shader ? shader->getID() : 0; + BatchKey newKey = {shaderID, textureID, (uint32_t)blendMode}; + + flushIfNeeded(newKey, shader, texture); + + glm::mat4 mat = transform.matrix; + uint16_t indexOffset = static_cast(currentBatch_.vertices.size()); + + for (int i = 0; i < 4; i++) { + Vertex v = quad.vertices[i]; + glm::vec4 pos(v.position.x, v.position.y, 0.0f, 1.0f); + glm::vec4 transformed = mat * pos; + v.position.x = transformed.x; + v.position.y = transformed.y; + + currentBatch_.vertices.push_back(v); + } + + currentBatch_.indices.push_back(indexOffset); + currentBatch_.indices.push_back(static_cast(indexOffset + 1)); + currentBatch_.indices.push_back(static_cast(indexOffset + 2)); + currentBatch_.indices.push_back(static_cast(indexOffset + 1)); + currentBatch_.indices.push_back(static_cast(indexOffset + 3)); + currentBatch_.indices.push_back(static_cast(indexOffset + 2)); +} + +void Batch::flush() { + if (!currentBatch_.vertices.empty()) { + flushCurrentBatch(); + } +} + +void Batch::flushIfNeeded(const BatchKey& newKey, Shader* shader, Ptr texture) { + if (currentBatch_.indices.empty()) { + currentBatch_.key = newKey; + currentBatch_.shader = shader; + currentBatch_.texture = texture; + return; + } + + if (currentBatch_.key.shaderID != newKey.shaderID || + currentBatch_.key.textureID != newKey.textureID || + currentBatch_.key.blendMode != newKey.blendMode) { + flushCurrentBatch(); + currentBatch_.key = newKey; + currentBatch_.shader = shader; + currentBatch_.texture = texture; + } +} + +void Batch::flushCurrentBatch() { + if (currentBatch_.vertices.empty()) { + return; + } + + glBindVertexArray(vao_); + + // 绑定着色器和纹理 + if (currentBatch_.shader) { + currentBatch_.shader->use(); + } + if (currentBatch_.texture) { + currentBatch_.texture->bind(); + } + + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glBufferSubData(GL_ARRAY_BUFFER, 0, + currentBatch_.vertices.size() * sizeof(Vertex), + currentBatch_.vertices.data()); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, + currentBatch_.indices.size() * sizeof(uint16), + currentBatch_.indices.data()); + + glDrawElements(GL_TRIANGLES, + static_cast(currentBatch_.indices.size()), + GL_UNSIGNED_SHORT, 0); + + currentBatch_.vertices.clear(); + currentBatch_.indices.clear(); + currentBatch_.shader = nullptr; + currentBatch_.texture.reset(); +} + +} diff --git a/Frostbite2D/src/frostbite2D/graphics/camera.cpp b/Frostbite2D/src/frostbite2D/graphics/camera.cpp new file mode 100644 index 0000000..5c88a28 --- /dev/null +++ b/Frostbite2D/src/frostbite2D/graphics/camera.cpp @@ -0,0 +1,46 @@ +#include +#include + +namespace frostbite2D { + +Camera::Camera() : position_(0, 0), zoom_(1.0f) {} + +void Camera::setPosition(const Vec2 &pos) { position_ = pos; } + +void Camera::setZoom(float zoom) { zoom_ = zoom; } + +void Camera::setViewport(int width, int height) { + viewportWidth_ = width; + viewportHeight_ = height; +} + +void Camera::lookAt(const Vec2 &target) { position_ = target; } + +void Camera::move(const Vec2 &delta) { position_ = position_ + delta; } + +void Camera::zoomAt(float factor, const Vec2 &screenPos) { + Vec2 worldBefore = screenPos - position_; + zoom_ *= factor; + Vec2 worldAfter = screenPos - position_; + position_ = position_ + (worldBefore - worldAfter); +} + +glm::mat4 Camera::getViewMatrix() const { + glm::mat4 view = glm::mat4(1.0f); + view[3][0] = -position_.x; + view[3][1] = -position_.y; + view[0][0] = zoom_; + view[1][1] = zoom_; + return view; +} + +glm::mat4 Camera::getProjectionMatrix() const { + float left = 0.0f; + float right = static_cast(viewportWidth_); + float bottom = static_cast(viewportHeight_); + float top = 0.0f; + + return glm::ortho(left, right, bottom, top, -1.0f, 1.0f); +} + +} // namespace frostbite2D \ No newline at end of file diff --git a/Frostbite2D/src/frostbite2D/graphics/renderer.cpp b/Frostbite2D/src/frostbite2D/graphics/renderer.cpp new file mode 100644 index 0000000..88f1bf4 --- /dev/null +++ b/Frostbite2D/src/frostbite2D/graphics/renderer.cpp @@ -0,0 +1,200 @@ +#include "SDL_log.h" +#include +#include +#include +#include + +namespace frostbite2D { + +Renderer& Renderer::get() { + static Renderer instance; + return instance; +} + +Renderer::Renderer() = default; + +Renderer::~Renderer() { + shutdown(); +} + +bool Renderer::init() { + if (initialized_) { + return true; + } + + if (!batch_.init()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize batch system"); + return false; + } + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + SDL_Log("Renderer initialized"); + initialized_ = true; + return true; +} + +void Renderer::shutdown() { + batch_.shutdown(); + shaderManager_.shutdown(); + initialized_ = false; +} + +void Renderer::beginFrame() { + glViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_); + glClearColor(clearColor_[0] / 255.0f, clearColor_[1] / 255.0f, + clearColor_[2] / 255.0f, clearColor_[3] / 255.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + updateUniforms(); + batch_.begin(); +} + +void Renderer::endFrame() { + batch_.end(); +} + +void Renderer::flush() { + batch_.flush(); +} + +void Renderer::setViewport(int x, int y, int width, int height) { + viewportX_ = x; + viewportY_ = y; + viewportWidth_ = width; + viewportHeight_ = height; + + if (camera_) { + camera_->setViewport(width, height); + } +} + +void Renderer::setClearColor(float r, float g, float b, float a) { + clearColor_[0] = static_cast(r * 255); + clearColor_[1] = static_cast(g * 255); + clearColor_[2] = static_cast(b * 255); + clearColor_[3] = static_cast(a * 255); +} + +void Renderer::setClearColor(const Color& color) { + clearColor_[0] = color.r; + clearColor_[1] = color.g; + clearColor_[2] = color.b; + clearColor_[3] = color.a; +} + +void Renderer::clear(uint32_t flags) { + glClear(flags); +} + +void Renderer::setCamera(Camera* camera) { + camera_ = camera; + if (camera) { + camera_->setViewport(viewportWidth_, viewportHeight_); + } +} + +void Renderer::setupBlendMode(BlendMode mode) { + switch (mode) { + case BlendMode::None: + glDisable(GL_BLEND); + break; + case BlendMode::Normal: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case BlendMode::Additive: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + break; + case BlendMode::Subtractive: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + break; + case BlendMode::Multiply: + glEnable(GL_BLEND); + glBlendFunc(GL_DST_COLOR, GL_ZERO); + break; + } +} + +void Renderer::updateUniforms() { + if (camera_) { + auto* spriteShader = shaderManager_.getShader("sprite"); + if (spriteShader) { + spriteShader->use(); + spriteShader->setMat4("u_view", camera_->getViewMatrix()); + spriteShader->setMat4("u_projection", camera_->getProjectionMatrix()); + } + + auto* coloredShader = shaderManager_.getShader("colored_quad"); + if (coloredShader) { + coloredShader->use(); + coloredShader->setMat4("u_view", camera_->getViewMatrix()); + coloredShader->setMat4("u_projection", camera_->getProjectionMatrix()); + } + } +} + +void Renderer::drawQuad(const Vec2& pos, const Size& size, float cr, float cg, + float cb, float ca) { + Rect rect(pos.x, pos.y, size.width, size.height); + Quad quad = Quad::create(rect, cr, cg, cb, ca); + Transform2D transform = Transform2D::identity(); + + auto* shader = shaderManager_.getShader("colored_quad"); + if (shader) { + shader->use(); + } + + batch_.submitQuad(quad, transform, nullptr, shader, BlendMode::Normal); +} + +void Renderer::drawQuad(const Vec2& pos, const Size& size, Ptr texture) { + Rect rect(pos.x, pos.y, size.width, size.height); + Quad quad = Quad::createTextured(rect, Rect(0, 0, size.width, size.height), + Vec2(size.width, size.height)); + Transform2D transform = Transform2D::identity(); + + auto* shader = shaderManager_.getShader("sprite"); + if (shader) { + shader->use(); + } + + batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal); +} + +void Renderer::drawQuad(const Rect& rect, const Color& color) { + drawQuad(Vec2(rect.left(), rect.top()), + Size(rect.width(), rect.height()), + static_cast(color.r) / 255.0f, + static_cast(color.g) / 255.0f, + static_cast(color.b) / 255.0f, + static_cast(color.a) / 255.0f); +} + +void Renderer::drawSprite(const Vec2& pos, const Size& size, Ptr texture) { + drawQuad(pos, size, texture); +} + +void Renderer::drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize, + Ptr texture, const Color& color) { + Rect destRect(pos.x, pos.y, srcRect.width(), srcRect.height()); + Quad quad = Quad::createTextured(destRect, srcRect, texSize, + static_cast(color.r) / 255.0f, + static_cast(color.g) / 255.0f, + static_cast(color.b) / 255.0f, + static_cast(color.a) / 255.0f); + Transform2D transform = Transform2D::identity(); + + auto* shader = shaderManager_.getShader("sprite"); + if (shader) { + shader->use(); + } + + batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal); +} + +} \ No newline at end of file diff --git a/Frostbite2D/src/frostbite2D/graphics/shader.cpp b/Frostbite2D/src/frostbite2D/graphics/shader.cpp new file mode 100644 index 0000000..a9b3b9d --- /dev/null +++ b/Frostbite2D/src/frostbite2D/graphics/shader.cpp @@ -0,0 +1,144 @@ +#include "SDL_log.h" +#include +#include +#include +#include +#include + +namespace frostbite2D { + +Shader::Shader(const std::string& name, uint32_t programID) + : name_(name), programID_(programID) {} + +Shader::~Shader() { + if (programID_ != 0) { + glDeleteProgram(programID_); + programID_ = 0; + } + uniformLocations_.clear(); +} + +void Shader::use() { + glUseProgram(programID_); +} + +void Shader::unuse() { + glUseProgram(0); +} + +bool Shader::compile(uint32_t type, const std::string& source) { + uint32_t shaderID = glCreateShader(type); + const char* src = source.c_str(); + glShaderSource(shaderID, 1, &src, nullptr); + glCompileShader(shaderID); + + int success; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); + if (!success) { + int length; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &length); + std::vector log(length); + glGetShaderInfoLog(shaderID, length, &length, log.data()); + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader compilation failed: %s", log.data()); + glDeleteShader(shaderID); + return false; + } + + return true; +} + +bool Shader::link(uint32_t vertexID, uint32_t fragmentID) { + programID_ = glCreateProgram(); + glAttachShader(programID_, vertexID); + glAttachShader(programID_, fragmentID); + glLinkProgram(programID_); + + int success; + glGetProgramiv(programID_, GL_LINK_STATUS, &success); + if (!success) { + int length; + glGetProgramiv(programID_, GL_INFO_LOG_LENGTH, &length); + std::vector log(length); + glGetProgramInfoLog(programID_, length, &length, log.data()); + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader linking failed: %s", log.data()); + glDeleteProgram(programID_); + programID_ = 0; + return false; + } + + glDeleteShader(vertexID); + glDeleteShader(fragmentID); + + return true; +} + +int Shader::getUniformLocation(const std::string& name) { + auto it = uniformLocations_.find(name); + if (it != uniformLocations_.end()) { + return it->second; + } + + int location = glGetUniformLocation(programID_, name.c_str()); + uniformLocations_[name] = location; + return location; +} + +void Shader::setInt(const std::string& name, int value) { + int location = getUniformLocation(name); + if (location >= 0) { + glUniform1i(location, value); + } +} + +void Shader::setFloat(const std::string& name, float value) { + int location = getUniformLocation(name); + if (location >= 0) { + glUniform1f(location, value); + } +} + +void Shader::setVec2(const std::string& name, const Vec2& value) { + int location = getUniformLocation(name); + if (location >= 0) { + glUniform2f(location, value.x, value.y); + } +} + +void Shader::setVec3(const std::string& name, const glm::vec3& value) { + int location = getUniformLocation(name); + if (location >= 0) { + glUniform3f(location, value.x, value.y, value.z); + } +} + +void Shader::setVec4(const std::string& name, const glm::vec4& value) { + int location = getUniformLocation(name); + if (location >= 0) { + glUniform4f(location, value.x, value.y, value.z, value.w); + } +} + +void Shader::setMat3(const std::string& name, const glm::mat3& value) { + int location = getUniformLocation(name); + if (location >= 0) { + glUniformMatrix3fv(location, 1, GL_FALSE, &value[0][0]); + } +} + +void Shader::setMat4(const std::string& name, const glm::mat4& value) { + int location = getUniformLocation(name); + if (location >= 0) { + glUniformMatrix4fv(location, 1, GL_FALSE, &value[0][0]); + } +} + +void Shader::setTexture(const std::string& name, int slot) { + int location = getUniformLocation(name); + if (location >= 0) { + glUniform1i(location, slot); + } +} + +} \ No newline at end of file diff --git a/Frostbite2D/src/frostbite2D/graphics/shader_manager.cpp b/Frostbite2D/src/frostbite2D/graphics/shader_manager.cpp new file mode 100644 index 0000000..941b97d --- /dev/null +++ b/Frostbite2D/src/frostbite2D/graphics/shader_manager.cpp @@ -0,0 +1,186 @@ +#include "SDL_log.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +using json = nlohmann::json; + +namespace frostbite2D { + +ShaderManager& ShaderManager::get() { + static ShaderManager instance; + return instance; +} + +ShaderManager::~ShaderManager() { + shutdown(); +} + +bool ShaderManager::init(const std::string& shadersDir) { + shadersDir_ = shadersDir; + shaders_.clear(); + + std::string configPath = shadersDir_ + "/shaders.json"; + return loadShadersFromConfig(configPath); +} + +void ShaderManager::shutdown() { + shaders_.clear(); +} + +Shader* ShaderManager::getShader(const std::string& name) { + auto it = shaders_.find(name); + if (it != shaders_.end()) { + return it->second.get(); + } + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Shader not found: %s", name.c_str()); + return nullptr; +} + +bool ShaderManager::hasShader(const std::string& name) const { + return shaders_.find(name) != shaders_.end(); +} + +bool ShaderManager::loadShader(const std::string& name, const std::string& vertPath, + const std::string& fragPath) { + Asset& asset = Asset::get(); + std::string vertSource, fragSource; + + if (!asset.readTextFile(vertPath, vertSource)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load vertex shader: %s", vertPath.c_str()); + return false; + } + + if (!asset.readTextFile(fragPath, fragSource)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load fragment shader: %s", fragPath.c_str()); + return false; + } + + uint32_t vertexID = glCreateShader(GL_VERTEX_SHADER); + uint32_t fragmentID = glCreateShader(GL_FRAGMENT_SHADER); + + const char* vertSrc = vertSource.c_str(); + glShaderSource(vertexID, 1, &vertSrc, nullptr); + glCompileShader(vertexID); + + const char* fragSrc = fragSource.c_str(); + glShaderSource(fragmentID, 1, &fragSrc, nullptr); + glCompileShader(fragmentID); + + int success; + glGetShaderiv(vertexID, GL_COMPILE_STATUS, &success); + if (!success) { + int length; + glGetShaderiv(vertexID, GL_INFO_LOG_LENGTH, &length); + std::vector log(length); + glGetShaderInfoLog(vertexID, length, &length, log.data()); + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Vertex shader compilation failed: %s", log.data()); + glDeleteShader(vertexID); + glDeleteShader(fragmentID); + return false; + } + + glGetShaderiv(fragmentID, GL_COMPILE_STATUS, &success); + if (!success) { + int length; + glGetShaderiv(fragmentID, GL_INFO_LOG_LENGTH, &length); + std::vector log(length); + glGetShaderInfoLog(fragmentID, length, &length, log.data()); + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Fragment shader compilation failed: %s", log.data()); + glDeleteShader(vertexID); + glDeleteShader(fragmentID); + return false; + } + + uint32_t programID = glCreateProgram(); + glAttachShader(programID, vertexID); + glAttachShader(programID, fragmentID); + + // 绑定顶点属性位置,确保和 Batch 类的设置一致 + glBindAttribLocation(programID, 0, "a_position"); + glBindAttribLocation(programID, 1, "a_texCoord"); + glBindAttribLocation(programID, 2, "a_color"); + + glLinkProgram(programID); + + glGetProgramiv(programID, GL_LINK_STATUS, &success); + if (!success) { + int length; + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &length); + std::vector log(length); + glGetProgramInfoLog(programID, length, &length, log.data()); + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader linking failed: %s", log.data()); + glDeleteProgram(programID); + glDeleteShader(vertexID); + glDeleteShader(fragmentID); + return false; + } + + glDeleteShader(vertexID); + glDeleteShader(fragmentID); + + auto shader = Ptr(new Shader(name, programID)); + shaders_[name] = shader; + + SDL_Log("Loaded shader: %s", name.c_str()); + return true; +} + +bool ShaderManager::loadShadersFromConfig(const std::string& configPath) { + Asset& asset = Asset::get(); + std::string content; + + if (!asset.readTextFile(configPath, content)) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "No shaders.json found, will compile shaders on demand"); + return true; + } + + try { + json config = json::parse(content); + + if (!config.contains("shaders")) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid shaders.json: missing 'shaders' key"); + return false; + } + + auto shaders = config["shaders"]; + for (auto& [key, value] : shaders.items()) { + if (!value.contains("vertex") || !value.contains("fragment")) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Invalid shader entry: %s", key.c_str()); + continue; + } + + std::string vertFile = value["vertex"]; + std::string fragFile = value["fragment"]; + + std::string vertPath = shadersDir_ + "/" + vertFile; + std::string fragPath = shadersDir_ + "/" + fragFile; + + if (!loadShader(key, vertPath, fragPath)) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to load shader: %s", key.c_str()); + } + } + + SDL_Log("Loaded %zu shaders from config", shaders_.size()); + return true; + + } catch (const json::parse_error& e) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to parse shaders.json: %s", e.what()); + return false; + } catch (const std::exception& e) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error loading shaders: %s", e.what()); + return false; + } +} + +} \ No newline at end of file diff --git a/Frostbite2D/src/frostbite2D/graphics/texture.cpp b/Frostbite2D/src/frostbite2D/graphics/texture.cpp new file mode 100644 index 0000000..dbacf16 --- /dev/null +++ b/Frostbite2D/src/frostbite2D/graphics/texture.cpp @@ -0,0 +1,125 @@ +#define STB_IMAGE_IMPLEMENTATION +#include "SDL_log.h" +#include +#include +#include +#include +#include + +namespace frostbite2D { + +Texture::Texture(int width, int height, uint32_t id) + : width_(width), height_(height), textureID_(id) {} + +Texture::~Texture() { + if (textureID_ != 0) { + glDeleteTextures(1, &textureID_); + textureID_ = 0; + } +} + +Ptr Texture::loadFromFile(const std::string& path) { + Asset& asset = Asset::get(); + std::string resolvedPath = asset.resolveAssetPath(path); + + int width, height, channels; + uint8* data = stbi_load(resolvedPath.c_str(), &width, &height, &channels, 0); + + if (!data) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load texture: %s", resolvedPath.c_str()); + return nullptr; + } + + uint32_t format = channels == 4 ? GL_RGBA : GL_RGB; + + uint32_t textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + stbi_image_free(data); + + auto texture = Ptr(new Texture(width, height, textureID)); + texture->path_ = resolvedPath; + texture->channels_ = channels; + + SDL_Log("Loaded texture: %s (%dx%d, %d channels)", resolvedPath.c_str(), width, height, channels); + + return texture; +} + +Ptr Texture::createFromMemory(uint8* data, int width, int height, int channels) { + if (!data) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture from memory: null data"); + return nullptr; + } + + uint32_t format = channels == 4 ? GL_RGBA : GL_RGB; + + uint32_t textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + + auto texture = Ptr(new Texture(width, height, textureID)); + texture->channels_ = channels; + + return texture; +} + +Ptr Texture::createEmpty(int width, int height) { + uint32_t textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + + return Ptr(new Texture(width, height, textureID)); +} + +void Texture::bind(uint32_t slot) { + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, textureID_); +} + +void Texture::unbind() { + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture::setWrapMode(uint32_t wrapS, uint32_t wrapT) { + bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); + unbind(); +} + +void Texture::setFilterMode(uint32_t minFilter, uint32_t magFilter) { + bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); + unbind(); +} + +} \ No newline at end of file diff --git a/Frostbite2D/src/main.cpp b/Frostbite2D/src/main.cpp index 06faaf7..92c54cb 100644 --- a/Frostbite2D/src/main.cpp +++ b/Frostbite2D/src/main.cpp @@ -2,8 +2,10 @@ #include #include #include +#include +#include +#include #include - #include using namespace frostbite2D; @@ -12,9 +14,9 @@ int main(int argc, char **argv) { AppConfig config = AppConfig::createDefault(); config.appName = "Frostbite2D Test App"; config.appVersion = "1.0.0"; - config.windowConfig.width = 800; - config.windowConfig.height = 600; - config.windowConfig.title = "Frostbite2D - Application Test"; + config.windowConfig.width = 1920; + config.windowConfig.height = 1080; + config.windowConfig.title = "Frostbite2D - Renderer Test"; Application &app = Application::get(); @@ -23,8 +25,38 @@ int main(int argc, char **argv) { return -1; } + auto &renderer = Renderer::get(); + renderer.setClearColor(0.1f, 0.1f, 0.15f); + + // 获取屏幕实际大小 + auto *window = app.getWindow(); + int screenWidth = window ? window->width() : config.windowConfig.width; + int screenHeight = window ? window->height() : config.windowConfig.height; + + // 设置 Renderer 的视口到屏幕大小 + renderer.setViewport(0, 0, screenWidth, screenHeight); + + // 相机使用游戏逻辑分辨率 (1280x720) + static Camera camera; + camera.setViewport(config.windowConfig.width, config.windowConfig.height); + renderer.setCamera(&camera); + + auto &asset = Asset::get(); +#ifdef __SWITCH__ + asset.setWorkingDirectory("/switch/testgame"); +#else + asset.setWorkingDirectory("."); +#endif + + // 初始化着色器管理器 + auto &shaderManager = renderer.getShaderManager(); + shaderManager.init("shaders"); + SDL_Log("Application initialized successfully"); - SDL_Log("Running main loop..."); + SDL_Log("Screen size: %dx%d", screenWidth, screenHeight); + SDL_Log("Render size: %dx%d", config.windowConfig.width, + config.windowConfig.height); + SDL_Log("Starting main loop..."); app.run(); @@ -32,4 +64,4 @@ int main(int argc, char **argv) { SDL_Log("Application exited normally"); return 0; -} \ No newline at end of file +} diff --git a/platform/windows.lua b/platform/windows.lua index f0f51af..35f4527 100644 --- a/platform/windows.lua +++ b/platform/windows.lua @@ -22,6 +22,7 @@ target("Frostbite2D") add_files(path.join(os.projectdir(), "Frostbite2D/src/**.cpp")) add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c")) add_includedirs(path.join(os.projectdir(), "Frostbite2D/include")) + add_includedirs(path.join(os.projectdir(), "Frostbite2D/include/json")) -- 添加资源文件(如果存在) local rc_file = path.join(os.projectdir(), "resources/app.rc") diff --git a/shaders/colored_quad.frag b/shaders/colored_quad.frag new file mode 100644 index 0000000..24096e5 --- /dev/null +++ b/shaders/colored_quad.frag @@ -0,0 +1,9 @@ +#ifdef GL_ES + precision mediump float; +#endif + +varying vec4 v_color; + +void main() { + gl_FragColor = v_color; +} \ No newline at end of file diff --git a/shaders/colored_quad.vert b/shaders/colored_quad.vert new file mode 100644 index 0000000..cf5582f --- /dev/null +++ b/shaders/colored_quad.vert @@ -0,0 +1,19 @@ +#ifdef GL_ES + precision mediump float; +#endif + +attribute vec2 a_position; +attribute vec2 a_texCoord; +attribute vec4 a_color; + +uniform mat4 u_view; +uniform mat4 u_projection; + +varying vec2 v_texCoord; +varying vec4 v_color; + +void main() { + gl_Position = u_projection * u_view * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} \ No newline at end of file diff --git a/shaders/sprite.frag b/shaders/sprite.frag new file mode 100644 index 0000000..1d909c4 --- /dev/null +++ b/shaders/sprite.frag @@ -0,0 +1,13 @@ +#ifdef GL_ES + precision mediump float; +#endif + +varying vec2 v_texCoord; +varying vec4 v_color; + +uniform sampler2D u_texture; + +void main() { + vec4 texColor = texture2D(u_texture, v_texCoord); + gl_FragColor = texColor * v_color; +} \ No newline at end of file diff --git a/shaders/sprite.vert b/shaders/sprite.vert new file mode 100644 index 0000000..50d1fb3 --- /dev/null +++ b/shaders/sprite.vert @@ -0,0 +1,20 @@ +#ifdef GL_ES + precision mediump float; +#endif + +attribute vec2 a_position; +attribute vec2 a_texCoord; +attribute vec4 a_color; + +uniform mat4 u_view; +uniform mat4 u_projection; +uniform sampler2D u_texture; + +varying vec2 v_texCoord; +varying vec4 v_color; + +void main() { + gl_Position = u_projection * u_view * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e6235b3 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,47 @@ +#include "SDL_log.h" +#include +#include +#include +#include +#include +#include + +using namespace frostbite2D; + +int main(int argc, char **argv) { + AppConfig AppConfig::createDefault(); + config.appName = "Frostbite2D Test App"; + config.appVersion = "1.0.0"; + config.windowConfig.width = 800; + config.windowConfig.height = 600; + config.windowConfig.title = "Frostbite2D - Renderer Test"; + + Application& app = Application::get(); + + if (!app.init(config)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize application!"); + return -1; + } + + auto& renderer = Renderer::get(); + if (!renderer.init()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize renderer!"); + app.shutdown(); + return -1; + } + + renderer.setClearColor(0.2f, 0.3f, 1.0f); + + auto& asset = Asset::get(); + asset.setWorkingDirectory("assets"); + + SDL_Log("Application initialized successfully"); + SDL_Log("Starting main loop..."); + + app.run(); + + app.shutdown(); + + SDL_Log("Application exited normally"); + return 0; +} \ No newline at end of file