feat(渲染): 添加Sprite类并实现基础渲染功能
添加Sprite类用于2D精灵渲染,支持纹理、着色器、颜色、翻转等属性 在Application中增加批处理渲染逻辑 在示例游戏中添加测试精灵
This commit is contained in:
65
Frostbite2D/include/frostbite2D/2d/sprite.h
Normal file
65
Frostbite2D/include/frostbite2D/2d/sprite.h
Normal 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";
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
182
Frostbite2D/src/frostbite2D/2d/sprite.cpp
Normal file
182
Frostbite2D/src/frostbite2D/2d/sprite.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -197,7 +197,12 @@ void Application::update() {
|
||||
}
|
||||
|
||||
void Application::render() {
|
||||
Renderer& renderer = Renderer::get();
|
||||
Batch& batch = renderer.getBatch();
|
||||
|
||||
batch.begin();
|
||||
SceneManager::get().Render();
|
||||
batch.end();
|
||||
|
||||
if (window_) {
|
||||
window_->swap();
|
||||
|
||||
BIN
Game/assets/player.png
Normal file
BIN
Game/assets/player.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <frostbite2D/scene/scene.h>
|
||||
#include <frostbite2D/scene/scene_manager.h>
|
||||
#include <frostbite2D/2d/sprite.h>
|
||||
|
||||
using namespace frostbite2D;
|
||||
|
||||
@@ -28,6 +29,13 @@ int main(int argc, char **argv) {
|
||||
|
||||
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.shutdown();
|
||||
|
||||
Reference in New Issue
Block a user