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() {
|
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
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.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();
|
||||||
|
|||||||
Reference in New Issue
Block a user