feat(渲染): 添加Sprite类并实现基础渲染功能

添加Sprite类用于2D精灵渲染,支持纹理、着色器、颜色、翻转等属性
在Application中增加批处理渲染逻辑
在示例游戏中添加测试精灵
This commit is contained in:
2026-03-17 16:01:22 +08:00
parent b35ecb197f
commit f0298504ac
5 changed files with 260 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
#pragma once
#include <frostbite2D/2d/actor.h>
#include <frostbite2D/graphics/texture.h>
#include <frostbite2D/graphics/shader.h>
#include <frostbite2D/graphics/types.h>
#include <string>
namespace frostbite2D {
class Sprite : public Actor {
public:
Sprite();
explicit Sprite(Ptr<Texture> texture);
virtual ~Sprite();
static Ptr<Sprite> createFromFile(const std::string& path);
static Ptr<Sprite> createFromMemory(uint8* data, int width, int height, int channels);
void Render() override;
void SetTexture(Ptr<Texture> texture);
Ptr<Texture> GetTexture() const { return texture_; }
void SetShader(const std::string& shaderName);
void SetDefaultShader();
const std::string& GetShaderName() const { return shaderName_; }
bool HasCustomShader() const { return !shaderName_.empty(); }
void SetSourceRect(const Rect& rect);
const Rect& GetSourceRect() const { return srcRect_; }
void SetColor(const Color& color);
void SetColor(float r, float g, float b, float a = 1.0f);
const Color& GetColor() const { return color_; }
void SetFlippedX(bool flipped);
void SetFlippedY(bool flipped);
bool IsFlippedX() const { return flippedX_; }
bool IsFlippedY() const { return flippedY_; }
void SetBlendMode(BlendMode mode);
BlendMode GetBlendMode() const { return blendMode_; }
void SetSizeToTexture();
private:
void updateTransform();
Shader* getActiveShader() const;
Quad createQuad() const;
Ptr<Texture> texture_;
std::string shaderName_;
Color color_ = Color(1.0f, 1.0f, 1.0f, 1.0f);
Rect srcRect_;
bool flippedX_ = false;
bool flippedY_ = false;
BlendMode blendMode_ = BlendMode::Normal;
Transform2D transform_;
static constexpr const char* DEFAULT_SHADER = "sprite";
};
}

View File

@@ -0,0 +1,182 @@
#include <frostbite2D/2d/sprite.h>
#include <frostbite2D/core/application.h>
#include <frostbite2D/graphics/renderer.h>
#include <SDL2/SDL.h>
#include <algorithm>
namespace frostbite2D {
Sprite::Sprite()
: Actor() {
}
Sprite::Sprite(Ptr<Texture> texture)
: Actor()
, texture_(texture) {
}
Sprite::~Sprite() {
}
Ptr<Sprite> Sprite::createFromFile(const std::string& path) {
Ptr<Texture> texture = Texture::loadFromFile(path);
if (!texture) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load texture: %s", path.c_str());
return nullptr;
}
auto sprite = MakePtr<Sprite>(texture);
sprite->SetSizeToTexture();
return sprite;
}
Ptr<Sprite> Sprite::createFromMemory(uint8* data, int width, int height, int channels) {
Ptr<Texture> texture = Texture::createFromMemory(data, width, height, channels);
if (!texture) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create texture from memory");
return nullptr;
}
auto sprite = MakePtr<Sprite>(texture);
sprite->SetSizeToTexture();
return sprite;
}
void Sprite::Render() {
if (!IsVisible()) {
RenderChildren();
return;
}
if (!texture_) {
RenderChildren();
return;
}
Renderer& renderer = Renderer::get();
Batch& batch = renderer.getBatch();
Shader* shader = getActiveShader();
if (!shader || !shader->isValid()) {
RenderChildren();
return;
}
updateTransform();
Quad quad = createQuad();
batch.submitQuad(quad, transform_, texture_, shader, blendMode_);
RenderChildren();
}
void Sprite::SetTexture(Ptr<Texture> texture) {
texture_ = texture;
}
void Sprite::SetShader(const std::string& shaderName) {
shaderName_ = shaderName;
}
void Sprite::SetDefaultShader() {
shaderName_.clear();
}
void Sprite::SetSourceRect(const Rect& rect) {
srcRect_ = rect;
}
void Sprite::SetColor(const Color& color) {
color_ = color;
}
void Sprite::SetColor(float r, float g, float b, float a) {
color_ = Color(r, g, b, a);
}
void Sprite::SetFlippedX(bool flipped) {
flippedX_ = flipped;
}
void Sprite::SetFlippedY(bool flipped) {
flippedY_ = flipped;
}
void Sprite::SetBlendMode(BlendMode mode) {
blendMode_ = mode;
}
void Sprite::SetSizeToTexture() {
if (texture_) {
SetSize(texture_->getWidth(), texture_->getHeight());
}
}
void Sprite::updateTransform() {
Vec2 pos = GetPosition();
float rotation = GetRotation();
Vec2 scale = GetScale();
transform_ = Transform2D::translation(pos.x, pos.y) *
Transform2D::rotation(rotation) *
Transform2D::scaling(scale.x, scale.y);
}
Shader* Sprite::getActiveShader() const {
Renderer& renderer = Renderer::get();
ShaderManager& shaderManager = renderer.getShaderManager();
if (!shaderName_.empty()) {
Shader* shader = shaderManager.getShader(shaderName_);
if (shader && shader->isValid()) {
return shader;
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Shader '%s' not found or invalid, fallback to default",
shaderName_.c_str());
}
}
return shaderManager.getShader(DEFAULT_SHADER);
}
Quad Sprite::createQuad() const {
Rect srcRect = srcRect_;
if (srcRect.empty()) {
srcRect = Rect(0, 0, texture_->getWidth(), texture_->getHeight());
}
Vec2 destPos(0, 0);
Vec2 destSizeVec = GetSize();
if (destSizeVec.x == 0) {
destSizeVec.x = srcRect.width();
}
if (destSizeVec.y == 0) {
destSizeVec.y = srcRect.height();
}
Rect destRect(0, 0, destSizeVec.x, destSizeVec.y);
Vec2 texSize(texture_->getWidth(), texture_->getHeight());
Quad quad = Quad::createTextured(
destRect, srcRect, texSize,
color_.r, color_.g, color_.b, color_.a
);
if (flippedX_) {
std::swap(quad.vertices[0].texCoord, quad.vertices[1].texCoord);
std::swap(quad.vertices[2].texCoord, quad.vertices[3].texCoord);
}
if (flippedY_) {
std::swap(quad.vertices[0].texCoord, quad.vertices[2].texCoord);
std::swap(quad.vertices[1].texCoord, quad.vertices[3].texCoord);
}
return quad;
}
}

View File

@@ -197,7 +197,12 @@ void Application::update() {
} }
void Application::render() { void Application::render() {
Renderer& renderer = Renderer::get();
Batch& batch = renderer.getBatch();
batch.begin();
SceneManager::get().Render(); SceneManager::get().Render();
batch.end();
if (window_) { if (window_) {
window_->swap(); window_->swap();

BIN
Game/assets/player.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

View File

@@ -8,6 +8,7 @@
#include <frostbite2D/scene/scene.h> #include <frostbite2D/scene/scene.h>
#include <frostbite2D/scene/scene_manager.h> #include <frostbite2D/scene/scene_manager.h>
#include <frostbite2D/2d/sprite.h>
using namespace frostbite2D; using namespace frostbite2D;
@@ -28,6 +29,13 @@ int main(int argc, char **argv) {
SDL_Log("Starting main loop..."); SDL_Log("Starting main loop...");
auto menuScene = MakePtr<Scene>();
SceneManager::get().PushScene(menuScene);
auto sprite = Sprite::createFromFile("assets\\player.png");
sprite->SetPosition(100, 100);
menuScene->AddActor(sprite);
app.run(); app.run();
app.shutdown(); app.shutdown();