修改底层渲染为OpenGL

This commit is contained in:
2025-10-23 15:21:12 +08:00
parent 1fe898e09c
commit f9a2300b5a
37 changed files with 2782 additions and 3761 deletions

View File

@@ -1,6 +1,8 @@
#include "Actor.h"
#include "EngineFrame/Scene/Scene.h"
#include "EngineCore/Game.h"
#include "EngineFrame/Component/RenderBase.h"
#include "EngineFrame/Render/RenderManager.h"
#include <algorithm>
Actor::Actor()
@@ -16,6 +18,22 @@ void Actor::Init()
addTag(Tag::UPDATE);
}
void Actor::Render()
{
// 如果有裁切视口
if (this->_CropViewportFlag)
{
RenderManager *renderer = Game::GetInstance().GetRenderer();
renderer->SetClipRect(&this->_CropViewport);
BaseNode::Render();
renderer->CloseClipRect();
}
else
{
BaseNode::Render();
}
}
void Actor::AddComponent(RefPtr<Component> Component)
{
BaseNode::AddChild(Component);
@@ -25,3 +43,21 @@ void Actor::RemoveComponent(RefPtr<Component> Component)
{
BaseNode::RemoveChild(Component);
}
void Actor::SetCropViewport(SDL_Rect CropViewport)
{
if (CropViewport.x == 0 && CropViewport.y == 0 && CropViewport.w == 0 && CropViewport.h == 0)
{
this->_CropViewportFlag = false;
}
else
{
this->_CropViewportFlag = true;
this->_CropViewport = CropViewport;
}
}
SDL_Rect Actor::GetCropViewport()
{
return this->_CropViewport;
}

View File

@@ -10,14 +10,23 @@ class Scene;
*/
class Actor : public BaseNode
{
private:
//裁切视口Flag
bool _CropViewportFlag = false;
//裁切视口
SDL_Rect _CropViewport = {0, 0, 0, 0};
public:
Actor();
public:
void Init() override;
void Render() override;
void AddComponent(RefPtr<Component> Component);
void RemoveComponent(RefPtr<Component> Component);
// void AddChild(RefPtr<Actor> child);
// void RemoveChild(RefPtr<Actor> child);
// 设置裁切视口(放在Actor里 他与他的子对象都会被裁切)
void SetCropViewport(SDL_Rect CropViewport);
// 获取裁切视口
SDL_Rect GetCropViewport();
};

View File

@@ -288,6 +288,11 @@ void BaseNode::SetAlpha(float alpha)
this->Alpha = alpha;
}
BaseNode *BaseNode::GetParent()
{
return this->m_Parent;
}
float BaseNode::GetAlpha()
{
return this->Alpha;

View File

@@ -132,4 +132,7 @@ public:
virtual void SetAlpha(float alpha);
// 获取透明度
float GetAlpha();
//获取父对象
BaseNode *GetParent();
};

View File

@@ -67,20 +67,20 @@ void Animation::Init(std::string AniPath)
// TODO 染色
// 判断是否有Als
// if (Asset_Script::GetInstance().GetFileInfo(AniPath + ".als"))
// {
// AniScriptParser::AlsInfo Info = AssetManager::GetInstance().StructAlsInfo(AniPath + ".als");
// if (Info.AniList.size() > 0)
// {
// std::string Dir = AniPath.substr(0, AniPath.find_last_of("/") + 1);
// for (auto &Ani : Info.AniList)
// {
// RefPtr<Animation> AlsAniObj = new Animation(Dir + Ani.second.path);
// AlsAniObj->SetRenderZOrder(Ani.second.layer[1]);
// AddChild(AlsAniObj);
// }
// }
// }
if (Asset_Script::GetInstance().GetFileInfo(AniPath + ".als"))
{
AniScriptParser::AlsInfo Info = AssetManager::GetInstance().StructAlsInfo(AniPath + ".als");
if (Info.AniList.size() > 0)
{
std::string Dir = AniPath.substr(0, AniPath.find_last_of("/") + 1);
for (auto &Ani : Info.AniList)
{
RefPtr<Animation> AlsAniObj = new Animation(Dir + Ani.second.path);
AlsAniObj->SetRenderZOrder(Ani.second.layer[1]);
AddChild(AlsAniObj);
}
}
}
FlushFrame(0);
}
@@ -133,20 +133,7 @@ void Animation::Update(float deltaTime)
void Animation::Render()
{
if (!Visible)
return;
// 是否裁切
if (CropFlag)
{
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
SDL_RenderSetClipRect(renderer, &CropRect);
Actor::Render();
SDL_RenderSetClipRect(renderer, NULL);
}
else
{
Actor::Render();
}
Actor::Render();
}
void Animation::OnAdded(BaseNode *node)
@@ -210,7 +197,7 @@ void Animation::FlushFrame(int Index)
// 线性减淡
if (FlagBuf.count("GRAPHIC_EFFECT_LINEARDODGE"))
{
CurrentFrame->SetBlendMode(SDL_BLENDMODE_ADD);
CurrentFrame->SetBlendMode(LINEARDODGE);
}
// 旋转
if (FlagBuf.count("IMAGE_ROTATE"))
@@ -328,22 +315,3 @@ void Animation::InterpolationLogic()
CurrentFrame->SetRotation(OldAngleData + (NewAngleData - OldAngleData) * InterRate);
}
}
void Animation::SetClipRect(SDL_Rect clipRect)
{
CropFlag = true;
CropRect = clipRect;
}
void Animation::UnsetClipRect()
{
CropFlag = false;
}
void Animation::SetShadow()
{
for (auto &Sp : SpriteArr)
{
Sp->SetShadow();
}
}

View File

@@ -43,13 +43,6 @@ public:
// TODO SetDye
// TODO SetCrop
// 设置裁切区域
void SetClipRect(SDL_Rect clipRect);
// 取消裁切区域
void UnsetClipRect();
// 设置阴影
void SetShadow();
public:
// Ani是否可用
bool IsUsability = true;
@@ -95,10 +88,6 @@ public:
// DyeFrameList = null;
// // 整体染色
// DyeAllFlag = false;
// // 裁切数据
SDL_Rect CropRect = {0, 0, 0, 0};
// 裁切Flag
bool CropFlag = false;
// 附加选项
std::function<std::string(std::string, Animation::ReplaceData)> AdditionalOptions;

View File

@@ -3,13 +3,6 @@
AnimationManager::AnimationManager()
{
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
m_Texture = SDL_CreateTexture(
renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_TARGET,
Game::GetInstance().Screen_W,
Game::GetInstance().Screen_H);
}
void AnimationManager::PreRender()
@@ -65,35 +58,7 @@ void AnimationManager::PreRender()
void AnimationManager::Render()
{
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
// 设定渲染目标为纹理
SDL_SetRenderTarget(renderer, m_Texture);
SDL_RenderClear(renderer);
// 渲染所有子对象
Actor::Render();
// 恢复渲染目标为默认窗口
SDL_SetRenderTarget(renderer, nullptr);
SDL_Rect srcRect = {
static_cast<int>(m_RenderRect.x), // 子对象包围盒左上角X纹理中的位置
static_cast<int>(m_RenderRect.y), // 子对象包围盒左上角Y纹理中的位置
static_cast<int>(m_RenderRect.w), // 包围盒宽度(有效区域宽度)
static_cast<int>(m_RenderRect.h) // 包围盒高度(有效区域高度)
};
// SDL_FRect renderToWindow = {
// m_RenderRect.x, // 窗口上的X位置可自定义
// m_RenderRect.y, // 窗口上的Y位置可自定义
// m_RenderRect.w, // 渲染到窗口的宽度(与源区域等大)
// m_RenderRect.h // 渲染到窗口的高度(与源区域等大)
// };
SDL_FRect renderToWindow = {
0, 0, 1080, 720};
// SDL_Log("m_Texture rect: %f %f %f %f", renderToWindow.x, renderToWindow.y, renderToWindow.w, renderToWindow.h);
// SDL_RenderCopyF(renderer, m_Texture, &srcRect, &renderToWindow);
SDL_RenderCopyF(renderer, m_Texture, NULL, &renderToWindow);
}
void AnimationManager::AddAnimation(RefPtr<Animation> ani)

View File

@@ -0,0 +1,62 @@
#include "Canvas.h"
#include "EngineCore/Game.h"
#include "EngineFrame/Render/RenderManager.h"
Canvas::Canvas(VecSize size)
{
m_texture = new Texture();
m_texture->Init(size);
m_sprite = new Sprite();
m_sprite->SetTexture(m_texture);
Actor::Init();
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
// 将目标纹理附加到FBO的颜色附着点
glFramebufferTexture2D(
GL_FRAMEBUFFER, // 帧缓冲类型
GL_COLOR_ATTACHMENT0, // 颜色附着点(可多个,这里用第一个)
GL_TEXTURE_2D, // 纹理类型
m_texture->getID(), // 目标纹理ID
0 // 多级渐远纹理级别
);
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑FBO
}
void Canvas::PreRender()
{
if (m_dirty)
{
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); // 绑定FBO
// glViewport(0, 0, m_texture->getSize().width, m_texture->getSize().height);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 全透明黑色
glClear(GL_COLOR_BUFFER_BIT);
Actor::PreRender();
}
else
{
m_sprite->PreRender();
}
}
void Canvas::Render()
{
if (m_dirty)
{
Actor::Render();
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑FBO
// glViewport(0, 0, Game::GetInstance().Screen_W, Game::GetInstance().Screen_H);
m_dirty = false;
}
RenderManager *renderer = Game::GetInstance().GetRenderer();
renderer->SetCurrentShaderProgram("flip_y");
m_sprite->Render();
renderer->SetCurrentShaderProgram("normal");
}
void Canvas::AddChild(RefPtr<BaseNode> child)
{
m_dirty = true;
Actor::AddChild(child);
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "EngineFrame/Actor/Actor.h"
#include "EngineFrame/Render/Texture.h"
#include "EngineFrame/Component/Sprite.h"
class Canvas : public Actor
{
private:
/**纹理 */
RefPtr<Texture> m_texture = nullptr;
/**精灵 */
RefPtr<Sprite> m_sprite = nullptr;
/**脏标记 */
bool m_dirty = true;
/**FBO */
GLuint m_fbo = 0;
public:
Canvas(VecSize size);
void PreRender() override;
void Render() override;
void AddChild(RefPtr<BaseNode> child) override;
};

View File

@@ -1,25 +1,35 @@
#include "Sprite.h"
#include "EngineCore/Game.h"
#include "Text.h"
#include "EngineFrame/Render/RenderManager.h"
void Sprite::Init()
{
this->Size = m_texture->TextureSize;
this->Size = m_texture->getSize();
RenderBase::Init();
}
void Sprite::SetTexture(RefPtr<Texture> texture)
{
m_texture = texture;
Init();
CalcRenderInfoLogic(); // 第一次计算
}
Sprite::Sprite(std::string imgPath, int Index)
{
this->imgPath = imgPath;
this->Index = Index;
m_texture = new Texture(imgPath, Index);
m_texture = new Texture();
m_texture->Init(imgPath, Index);
Init();
CalcRenderInfoLogic(); // 第一次计算
}
Sprite::Sprite(std::string PngPath)
{
m_texture = new Texture(PngPath);
m_texture = new Texture();
m_texture->Init(PngPath);
Init();
CalcRenderInfoLogic(); // 第一次计算
}
@@ -35,6 +45,19 @@ RefPtr<Texture> Sprite::GetTexture()
void Sprite::CalcRenderInfoLogic()
{
// 获取至在最终的父对象检查是否显示
BaseNode *parentNode = this->GetParent();
while (parentNode != nullptr)
{
if (parentNode->Visible == false)
{
_RenderGuidanceInfo.Visible = false;
return;
}
parentNode = parentNode->GetParent();
}
_RenderGuidanceInfo.Visible = true;
// 计算缩放因子和翻转状态
float scaleX = transformIter.scale.x * transform.scale.x;
float scaleY = transformIter.scale.y * transform.scale.y;
@@ -51,8 +74,10 @@ void Sprite::CalcRenderInfoLogic()
_RenderGuidanceInfo.flip = static_cast<SDL_RendererFlip>(SDL_FLIP_VERTICAL | _RenderGuidanceInfo.flip);
// 纹理数据里带的偏移数据 有这个偏移才能保证动画播放时视觉中心点在一个点上
float texturePosX = flipX ? -(m_texture->TextureSize.width + m_texture->TexturePos.x) + SDL_abs(transform.position.x * 2) : m_texture->TexturePos.x;
float texturePosY = flipY ? -(m_texture->TextureSize.height + m_texture->TexturePos.y) + SDL_abs(transform.position.y * 2) : m_texture->TexturePos.y;
auto T_Size = m_texture->getSize();
auto T_Pos = m_texture->getPos();
float texturePosX = flipX ? -(T_Size.width + T_Pos.x) + SDL_abs(transform.position.x * 2) : T_Pos.x;
float texturePosY = flipY ? -(T_Size.height + T_Pos.y) + SDL_abs(transform.position.y * 2) : T_Pos.y;
// 先计算Img坐标与精灵坐标合成后的真实坐标
float RealPosX = transform.position.x + texturePosX;
@@ -93,13 +118,9 @@ void Sprite::CalcRenderInfoLogic()
// 更新渲染信息
_RenderGuidanceInfo.rect = {Xpos, Ypos, scaledWidth, scaledHeight};
// 如果有阴影 计算阴影的渲染信息
if (ShadowFlag)
CalculateShadowData();
_RenderGuidanceInfo.AnchorPos = {scaledAnchorOffsetX, scaledAnchorOffsetY};
// 设置纹理透明度
SDL_SetTextureAlphaMod(m_texture->GetTexture(), this->Alpha * 255);
m_texture->setAlpha(this->Alpha);
// 屏幕内检测
int screenWidth = Game::GetInstance().Screen_W;
@@ -113,17 +134,6 @@ void Sprite::CalcRenderInfoLogic()
CalcRenderInfoFlag = false;
}
void Sprite::SetClipRect(SDL_Rect clipRect)
{
CropFlag = true;
CropRect = clipRect;
}
void Sprite::UnsetClipRect()
{
CropFlag = false;
}
void Sprite::PreRender()
{
if (CalcRenderInfoFlag && Visible)
@@ -134,32 +144,26 @@ void Sprite::Render()
{
if (!Visible)
return;
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
if (!m_texture)
return;
RenderManager *renderer = Game::GetInstance().GetRenderer();
if (_RenderGuidanceInfo.IsInScreen && _RenderGuidanceInfo.Visible)
{
// 如果有阴影绘制阴影
if (ShadowFlag)
{
std::array<int, 6> indices = {0, 1, 2, 1, 3, 2};
SDL_RenderGeometry(renderer, m_texture->GetTexture(), vertices.data(), vertices.size(), indices.data(), indices.size());
}
SDL_FPoint AnchorPos = _RenderGuidanceInfo.AnchorPos;
// 混合
if (this->_BlendMode != NONE)
Blend();
SDL_Rect srcrect = {0, 0, this->Size.width, this->Size.height};
renderer->DrawTexture(m_texture, &srcrect, &_RenderGuidanceInfo.rect, _RenderGuidanceInfo.rotation, &AnchorPos, _RenderGuidanceInfo.flip);
// 还原混合
if (this->_BlendMode != NONE)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (_RenderGuidanceInfo.rotation != 0.f || _RenderGuidanceInfo.flip != SDL_FLIP_NONE)
{
SDL_FPoint AnchorPos = _RenderGuidanceInfo.AnchorPos;
SDL_RenderCopyExF(renderer, m_texture->GetTexture(), NULL, &_RenderGuidanceInfo.rect, _RenderGuidanceInfo.rotation, &AnchorPos, _RenderGuidanceInfo.flip);
}
else
{
SDL_RenderCopyF(renderer, m_texture->GetTexture(), NULL, &_RenderGuidanceInfo.rect);
// // 设置绘制颜色
// SDL_SetRenderDrawColor(renderer, 255, 0, 0, 128);
// // 绘制填充矩形
// SDL_RenderFillRectF(renderer, &_RenderGuidanceInfo.rect);
}
Game::GetInstance().m_RenderCount++;
}
}
@@ -168,72 +172,22 @@ void Sprite::Clear()
{
}
void Sprite::SetBlendMode(SDL_BlendMode blendMode)
void Sprite::SetBlendMode(LE_BlEND_MODE mode)
{
if (GetBlendMode() != blendMode)
m_texture->SetBlendMode(blendMode);
this->_BlendMode = mode;
}
SDL_BlendMode Sprite::GetBlendMode()
void Sprite::Blend()
{
return m_texture->GetBlendMode();
}
void Sprite::SetShadow()
{
ShadowFlag = true;
CalcRenderInfo();
}
void Sprite::CalculateShadowData()
{
const float shearX = 1.3f;
SDL_FRect guidance = _RenderGuidanceInfo.rect;
float shadowW = guidance.w; // 影子宽度(建议与角色宽度一致)
float shadowH = guidance.h * 0.33; // 影子高度可根据需求调整比如角色高度的1/4
// 计算角色脚部中点坐标(底部中心,即需要重叠的锚点)
float roleFootX = guidance.x + guidance.w / 2.0f + 3; // 角色X中心 + 位置偏移
float roleFootY = guidance.y + guidance.h - 5; // 角色底部Y坐标脚的位置
// 计算错切后的顶点(局部坐标)
vertices = {
// 左上角(局部坐标)
SDL_Vertex{{0.0f, 0.0f}, {0, 0, 0, 110}, {0.0f, 0.0f}},
// 右上角(局部坐标)
SDL_Vertex{{shadowW, 0.0f}, {0, 0, 0, 110}, {1.0f, 0.0f}},
// 左下角(局部坐标)
SDL_Vertex{{0.0f + shearX * shadowH, shadowH}, {0, 0, 0, 110}, {0.0f, 1.0f}},
// 右下角(局部坐标)
SDL_Vertex{{shadowW + shearX * shadowH, shadowH}, {0, 0, 0, 110}, {1.0f, 1.0f}}};
bool flipHorizontal = (_RenderGuidanceInfo.flip & SDL_FLIP_HORIZONTAL) != 0; // 检查水平翻转标志
bool flipVertical = (_RenderGuidanceInfo.flip & SDL_FLIP_VERTICAL) != 0; // 检查垂直翻转标志
// 应用纹理坐标翻转(核心逻辑)
for (auto &v : vertices)
switch (this->_BlendMode)
{
// 水平翻转反转u坐标0→11→0
if (flipHorizontal)
{
v.tex_coord.x = 1.0f - v.tex_coord.x;
}
// 垂直翻转反转v坐标0→11→0
if (flipVertical)
{
v.tex_coord.y = 1.0f - v.tex_coord.y;
}
}
// 计算影子底部中点并对齐角色脚部(与之前逻辑一致)
float shadowFootX = (vertices[2].position.x + vertices[3].position.x) / 2.0f;
float shadowFootY = shadowH;
float offsetX = roleFootX - shadowFootX;
float offsetY = roleFootY - shadowFootY;
// 应用位置偏移
for (auto &v : vertices)
{
v.position.x += offsetX;
v.position.y += offsetY;
case LINEARDODGE:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
break;
}
}
LE_BlEND_MODE Sprite::GetBlendMode()
{
return this->_BlendMode;
}

View File

@@ -8,7 +8,7 @@ class Game;
/**
* @brief Sprite类继承自Component类用于表示游戏中的精灵组件
*/
class Sprite : public RenderBase
class Sprite : public RenderBase
{
protected:
RefPtr<Texture> m_texture = nullptr;
@@ -22,36 +22,26 @@ public:
void PreRender() override;
void Clear() override;
void Init() override;
void SetTexture(RefPtr<Texture> texture);
RefPtr<Texture> GetTexture();
public:
// 渲染信息
RenderGuidanceInfo _RenderGuidanceInfo;
// 裁切数据
SDL_Rect CropRect = {0, 0, 0, 0};
// 裁切Flag
bool CropFlag = false;
// 阴影数据
std::array<SDL_Vertex, 4> vertices;
// 阴影Flag
bool ShadowFlag = false;
// 混合模式
LE_BlEND_MODE _BlendMode = NONE;
std::string imgPath;
int Index;
public:
// 计算渲染信息
void CalcRenderInfoLogic();
// 设置裁切区域
void SetClipRect(SDL_Rect clipRect);
// 取消裁切区域
void UnsetClipRect();
// 设置混合模式
void SetBlendMode(SDL_BlendMode blendMode);
void SetBlendMode(LE_BlEND_MODE mode);
// 混合
void Blend();
// 获取混合模式
SDL_BlendMode GetBlendMode();
// 设置阴影
void SetShadow();
// 计算阴影数据
void CalculateShadowData();
LE_BlEND_MODE GetBlendMode();
};

View File

@@ -5,200 +5,56 @@ Text::Text()
{
}
Text::Text(std::string Str, TTF_Font *font, SDL_Color color)
{
m_text = Str;
m_font = font;
m_text_color = color;
Init(Str, font, color);
}
Text::Text(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize)
{
m_text = Str;
m_font = font;
m_text_color = textColor;
m_stroke_color = strokeColor;
m_stroke_size = strokeSize;
Init(Str, font, textColor, strokeColor, strokeSize);
}
Text::~Text()
{
}
void Text::Init(std::string Str, TTF_Font *font, SDL_Color color)
{
// 标记该组件需要渲染和更新
addTag(Tag::RENDER);
addTag(Tag::UPDATE);
// TTF_SetFontOutline(font, 1);
if (!m_texture)
{
this->m_font = font;
this->m_color = color;
}
// 先渲染为表面
SDL_Surface *textSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), color);
if (!textSurface)
{
SDL_LogError(0, "文字渲染为表面失败TTF_Error:%s", TTF_GetError());
}
// 转换为RGBA8888格式与OpenGL的GL_RGBA匹配
SDL_Surface *rgbaSurface = SDL_ConvertSurfaceFormat(
textSurface,
SDL_PIXELFORMAT_ABGR8888,
0);
SDL_FreeSurface(textSurface);
// 再将表面转换为纹理
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
m_texture = SDL_CreateTextureFromSurface(renderer, textSurface);
if (!m_texture)
{
SDL_LogError(0, "表面转换为纹理失败SDL_Error:%s", SDL_GetError());
}
// 设置纹理过滤模式为最近邻,避免缩放模糊
SDL_SetTextureScaleMode(m_texture, SDL_ScaleModeNearest);
Size.x = textSurface->w;
Size.y = textSurface->h;
TextureSize.x = textSurface->w;
TextureSize.y = textSurface->h;
RenderManager *renderer = Game::GetInstance().GetRenderer();
m_texture = new Texture;
m_texture->Init(rgbaSurface);
// 释放表面
SDL_FreeSurface(textSurface);
}
void Text::Init(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize)
{
// 先保存原始字体的轮廓设置
int originalOutline = TTF_GetFontOutline(font);
// 设置字体轮廓大小(描边宽度)
TTF_SetFontOutline(font, strokeSize);
// 渲染描边(使用描边颜色)
SDL_Surface *strokeSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), strokeColor);
if (!strokeSurface)
{
SDL_LogError(0, "描边渲染为表面失败TTF_Error:%s", TTF_GetError());
TTF_SetFontOutline(font, originalOutline); // 恢复原始设置
return;
}
// 恢复字体轮廓设置,用于渲染文字本身
TTF_SetFontOutline(font, 0);
// 渲染文字本身(使用文字颜色)
SDL_Surface *textSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), textColor);
if (!textSurface)
{
SDL_LogError(0, "文字渲染为表面失败TTF_Error:%s", TTF_GetError());
SDL_FreeSurface(strokeSurface);
TTF_SetFontOutline(font, originalOutline); // 恢复原始设置
return;
}
// 创建一个合并描边和文字的表面
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
// 计算最终纹理大小(描边会增加额外尺寸)
int finalWidth = strokeSurface->w;
int finalHeight = strokeSurface->h;
// 创建一个临时表面用于合并描边和文字
SDL_Surface *finalSurface = SDL_CreateRGBSurfaceWithFormat(
0, finalWidth, finalHeight, 32, SDL_PIXELFORMAT_RGBA32);
if (!finalSurface)
{
SDL_LogError(0, "创建最终表面失败SDL_Error:%s", SDL_GetError());
SDL_FreeSurface(textSurface);
SDL_FreeSurface(strokeSurface);
TTF_SetFontOutline(font, originalOutline);
return;
}
// 将描边绘制到最终表面
SDL_Rect strokeRect = {0, 0, strokeSurface->w, strokeSurface->h};
SDL_BlitSurface(strokeSurface, nullptr, finalSurface, &strokeRect);
// 计算文字在描边中间的位置
SDL_Rect textRect = {
strokeSize, // X偏移描边宽度
strokeSize, // Y偏移描边宽度
textSurface->w,
textSurface->h};
SDL_BlitSurface(textSurface, nullptr, finalSurface, &textRect);
// 将合并后的表面转换为纹理
m_texture = SDL_CreateTextureFromSurface(renderer, finalSurface);
if (!m_texture)
{
SDL_LogError(0, "表面转换为纹理失败SDL_Error:%s", SDL_GetError());
}
// 设置尺寸信息
Size.x = finalSurface->w;
Size.y = finalSurface->h;
TextureSize.x = finalSurface->w;
TextureSize.y = finalSurface->h;
// 释放所有临时表面
SDL_FreeSurface(textSurface);
SDL_FreeSurface(strokeSurface);
SDL_FreeSurface(finalSurface);
// 恢复字体原始轮廓设置
TTF_SetFontOutline(font, originalOutline);
}
void Text::HandleEvents(SDL_Event *e)
{
}
void Text::Update(float deltaTime)
{
SDL_FreeSurface(rgbaSurface);
Sprite::Init();
}
void Text::Render()
{
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
if (!renderer || !m_texture)
return;
SDL_Rect dstrect = {Pos.x, Pos.y, Size.x, Size.y};
SDL_RenderCopy(renderer, m_texture, NULL, &dstrect);
}
void Text::Clear()
{
}
void Text::SetPos(SDL_Point pos)
{
Pos = pos;
}
SDL_Point Text::GetPos()
{
return Pos;
Sprite::Render();
}
void Text::SetText(std::string Str)
{
if (Str == m_text)
return;
if (!m_font)
{
SDL_LogError(0, "SetText失败字体指针为空");
return;
}
// 如果有原纹理先删除原纹理
if (m_texture)
{
SDL_DestroyTexture(m_texture);
m_texture = nullptr; // 置空指针
}
m_text = Str;
// 根据是否有描边选择对应的Init方法
if (m_stroke_size > 0)
{
Init(Str, m_font, m_text_color, m_stroke_color, m_stroke_size);
}
else
{
Init(Str, m_font, m_text_color);
}
Init(Str, m_font, m_color);
}
std::string Text::GetText()

View File

@@ -1,59 +1,30 @@
#pragma once
#include <string>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "EngineFrame/Component/Component.h"
class Game;
#include "EngineFrame/Component/Sprite.h"
class Text : public Component
class Text : public Sprite
{
private:
/* data */
SDL_Texture *m_texture = nullptr;
public:
Text(/* args */);
Text(std::string Str, TTF_Font *font, SDL_Color color);
Text(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize);
~Text();
// 显式引入基类的Init方法避免隐藏
using Component::Init;
void Init(std::string Str, TTF_Font *font, SDL_Color color);
void Init(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize);
void HandleEvents(SDL_Event *e) override;
void Update(float deltaTime) override;
void Render() override;
void Clear() override;
SDL_Texture *GetTexture();
public:
// 组件标签
Tag m_tag = Tag::RENDER | Tag::UPDATE; // 标记该组件需要渲染和更新
/** 文本内容 */
std::string m_text;
/** 字体 */
TTF_Font *m_font;
SDL_Color m_text_color;
SDL_Color m_stroke_color;
int m_stroke_size = 0;
SDL_Point Pos = {0, 0}; // 位置坐标
SDL_Point TextureSize = {0, 0}; // 纹理大小
SDL_Point Size = {0, 0}; // 大小
SDL_Point Anchor = {0, 0}; // 中心点
float Angle = 0.0f; // 旋转角度
SDL_RendererFlip flip = SDL_FLIP_NONE; // 翻转
/** 字体颜色 */
SDL_Color m_color;
public:
// 设置坐标
void SetPos(SDL_Point pos);
// 设置文本
void SetText(std::string Str);
// 获取坐标
SDL_Point GetPos();
// 获取文本
std::string GetText();
};

View File

@@ -0,0 +1,359 @@
#include "RenderManager.h"
#include <fstream>
#include "Texture.h"
RenderManager::RenderManager(SDL_Window *window)
{
_window = window;
// 创建OpenGL上下文
_ctx = SDL_GL_CreateContext(window);
if (!_ctx)
{
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "GL context create failed: %s\n", SDL_GetError());
}
// 初始化GLAD
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress))
{
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "GLAD init failed!\n");
}
// 获取窗口大小
int width, height;
SDL_GetWindowSize(window, &width, &height);
// 设置视口
glViewport(0, 0, width, height);
// 设置窗口尺寸
_windowWidth = width;
_windowHeight = height;
// 设置正交投影矩阵
_orthoMatrix = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f);
// 设置清屏颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// 启用混合模式
glEnable(GL_BLEND);
// 默认设置标准alpha混合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 初始化着色器程序
InitShaderProgram();
// 初始化绘制2D纹理缓冲程序
Init2DTextureProgram();
// 设定默认使用的着色器程序和缓冲程序
SetCurrentShaderProgram("normal");
SetCurrentBufferObject("2DTexture");
}
RenderManager::~RenderManager()
{
// 释放着色器程序
for (auto &[name, program] : _shaderProgramMap)
{
glDeleteProgram(program);
}
// 释放缓冲对象
for (auto &[name, program] : _RenderParamsMap)
{
glDeleteVertexArrays(1, &program.VAO);
glDeleteBuffers(1, &program.VBO);
glDeleteBuffers(1, &program.EBO);
}
// 释放OpenGL上下文
SDL_GL_DeleteContext(_ctx);
}
void RenderManager::InitShaderProgram()
{
std::ifstream shaderFile("shader/config.json");
nlohmann::json shaderConfig;
shaderFile >> shaderConfig;
for (auto &[groupName, groupData] : shaderConfig["shader"].items())
{
if (!groupData.is_object())
{
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "着色器组 %s 格式错误,跳过\n", groupName.c_str());
continue;
}
if (!groupData.contains("vertex") || !groupData["vertex"].is_string())
{
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 缺少 vertex 路径,跳过\n", groupName.c_str());
continue;
}
if (!groupData.contains("fragment") || !groupData["fragment"].is_string())
{
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 缺少 fragment 路径,跳过\n", groupName.c_str());
continue;
}
std::string vertPath = groupData["vertex"].get<std::string>();
std::string fragPath = groupData["fragment"].get<std::string>();
GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertPath);
GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragPath);
if (vertexShader == 0 || fragmentShader == 0)
{
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 着色器编译失败,跳过\n", groupName.c_str());
continue;
}
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
GLint programLinksuccess;
glGetProgramiv(program, GL_LINK_STATUS, &programLinksuccess);
if (!programLinksuccess)
{
char infoLog[512];
glGetProgramInfoLog(program, 512, nullptr, infoLog);
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 着色器链接失败,失败原因: %s\n", groupName.c_str(), infoLog);
}
_shaderProgramMap[groupName] = program;
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
}
void RenderManager::Init2DTextureProgram()
{
// 初始化顶点数据(矩形的四个顶点)
float vertices[] = {
0.0f, 0.0f, 0.0f, 0.0f, // 左下(单位坐标)
1.0f, 0.0f, 1.0f, 0.0f, // 右下
1.0f, 1.0f, 1.0f, 1.0f, // 右上
0.0f, 1.0f, 0.0f, 1.0f // 左上
};
// 索引数组
unsigned int indices[] = {0, 1, 2, 2, 3, 0};
GLuint VAO, VBO, EBO;
// 创建VAO/VBO/EBO
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
// 创建完解绑
glBindVertexArray(0);
SetCurrentShaderProgram("normal");
glUseProgram(_currentShaderProgram);
// 获取uniform变量位置
GLuint _uModelLoc = glGetUniformLocation(_currentShaderProgram, "uModel");
GLuint _uViewProjLoc = glGetUniformLocation(_currentShaderProgram, "uViewProj");
GLuint _uTextureLoc = glGetUniformLocation(_currentShaderProgram, "uTexture");
if (_uModelLoc == -1 || _uViewProjLoc == -1 || _uTextureLoc == -1)
{
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "无法获取着色器 uniform 变量位置\n");
}
std::vector<GLuint> uniformLocs = {_uModelLoc, _uViewProjLoc, _uTextureLoc};
DrawLogicFunc drawFunc = [this](
RefPtr<Texture> textureObj, const SDL_Rect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, void *userdata)
{
GLuint texture = textureObj->getID();
if (!texture || !dstrect)
return;
glUseProgram(_currentShaderProgram);
glm::mat4 model = glm::mat4(1.0f);
// 平移
model = glm::translate(model, glm::vec3(dstrect->x, dstrect->y, 0.0f));
// 计算旋转中心
float centerX = center ? center->x : dstrect->w * 0.5f;
float centerY = center ? center->y : dstrect->h * 0.5f;
// 旋转中心平移
model = glm::translate(model, glm::vec3(centerX, centerY, 0.0f));
// 旋转
if (angle != 0.0)
{
model = glm::rotate(model, glm::radians(static_cast<float>(angle)), glm::vec3(0.0f, 0.0f, 1.0f));
}
// 旋转中心平移
model = glm::translate(model, glm::vec3(-centerX, -centerY, 0.0f));
// 缩放和翻转
float scaleX = dstrect->w;
float scaleY = dstrect->h;
// 应用翻转(通过负缩放实现)
if (flip & SDL_FLIP_HORIZONTAL)
scaleX = -scaleX;
if (flip & SDL_FLIP_VERTICAL)
scaleY = -scaleY;
model = glm::scale(model, glm::vec3(scaleX, scaleY, 1.0f));
// 翻转后的位置补偿
if (flip & SDL_FLIP_HORIZONTAL)
{
model = glm::translate(model, glm::vec3(-1.0f, 0.0f, 0.0f));
}
if (flip & SDL_FLIP_VERTICAL)
{
model = glm::translate(model, glm::vec3(0.0f, -1.0f, 0.0f));
}
// 设置着色器 uniforms
glUniformMatrix4fv(_currentRenderParams.UnimLocs[0], 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(_currentRenderParams.UnimLocs[1], 1, GL_FALSE, glm::value_ptr(_orthoMatrix));
// 绑定纹理并设置采样器
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(_currentRenderParams.UnimLocs[2], 0);
// 处理纹理裁剪
int texWidth = textureObj->getSize().width;
int texHeight = textureObj->getSize().height;
// 计算纹理坐标
float minu, minv, maxu, maxv;
if (srcrect)
{
minu = (float)srcrect->x / texWidth;
minv = (float)srcrect->y / texHeight;
maxu = (float)(srcrect->x + srcrect->w) / texWidth;
maxv = (float)(srcrect->y + srcrect->h) / texHeight;
}
else
{
minu = 0.0f;
minv = 0.0f;
maxu = 1.0f;
maxv = 1.0f;
}
// 计算顶点位置(单位矩形,通过模型变换进行缩放和平移)
float minx = 0.0f;
float miny = 0.0f;
float maxx = 1.0f;
float maxy = 1.0f;
// 创建顶点数据(位置 + 纹理坐标)
float vertices[] = {
// 位置 // 纹理坐标
minx, miny, minu, minv, // 左上
maxx, miny, maxu, minv, // 右上
maxx, maxy, maxu, maxv, // 右下
minx, maxy, minu, maxv // 左下
};
// 更新VBO数据
glBindBuffer(GL_ARRAY_BUFFER, _currentRenderParams.VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
// 绘制纹理
glBindVertexArray(_currentRenderParams.VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
};
GL_RenderParams bufferObject = {VAO, VBO, EBO, uniformLocs, drawFunc};
AddBufferObject("2DTexture", bufferObject);
}
void RenderManager::SetCurrentShaderProgram(std::string name)
{
if (_shaderProgramMap.find(name) != _shaderProgramMap.end())
{
this->_currentShaderProgram = _shaderProgramMap[name];
}
else
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到着色器程序: %s\n", name.c_str());
}
void RenderManager::SetCurrentBufferObject(std::string name)
{
if (_RenderParamsMap.find(name) != _RenderParamsMap.end())
{
this->_currentRenderParams = _RenderParamsMap[name];
}
else
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到渲染参数: %s\n", name.c_str());
}
void RenderManager::ClearScreen()
{
glClear(GL_COLOR_BUFFER_BIT);
}
void RenderManager::SwapBuffer()
{
SDL_GL_SwapWindow(_window);
}
void RenderManager::SetClipRect(const SDL_Rect *rect)
{
glEnable(GL_SCISSOR_TEST);
glScissor(rect->x, _windowHeight - rect->y - rect->h, rect->w, rect->h);
}
void RenderManager::CloseClipRect()
{
glDisable(GL_SCISSOR_TEST);
}
void RenderManager::DrawTexture(RefPtr<Texture> textureObj, const SDL_Rect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, void *userdata)
{
if (_currentRenderParams.DrawFunc != nullptr)
_currentRenderParams.DrawFunc(textureObj, srcrect, dstrect, angle, center, flip, userdata);
else
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到渲染逻辑函数\n");
}
GLuint RenderManager::CompileShader(GLenum type, std::string Path)
{
std::ifstream Source("shader/" + Path);
if (!Source.is_open())
{
SDL_LogError(0, "Failed to open shader file: %s\n", Path.c_str());
return 0;
}
std::string ShaderSource((std::istreambuf_iterator<char>(Source)), std::istreambuf_iterator<char>());
const char *SourcePtr = ShaderSource.c_str();
GLuint ShaderId = glCreateShader(type);
glShaderSource(ShaderId, 1, &SourcePtr, NULL);
glCompileShader(ShaderId);
// 检查编译错误
GLint success;
glGetShaderiv(ShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
char infoLog[512];
glGetShaderInfoLog(ShaderId, 512, nullptr, infoLog);
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Shader compile error: %s\n", infoLog);
glDeleteShader(ShaderId);
return 0;
}
return ShaderId;
}
void RenderManager::AddBufferObject(std::string name, GL_RenderParams bufferObject)
{
_RenderParamsMap[name] = bufferObject;
}

View File

@@ -0,0 +1,78 @@
#pragma once
#include <SDL.h>
#include <SDL_image.h>
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <map>
#include <string>
#include <json.hpp>
#include "EngineFrame/Render/Texture.h"
class RenderManager
{
public:
// 绘制纹理的逻辑函数
using DrawLogicFunc = std::function<void(RefPtr<Texture>, const SDL_Rect *, const SDL_FRect *,
double, const SDL_FPoint *, SDL_RendererFlip,
void *)>;
struct GL_RenderParams
{
GLuint VAO;
GLuint VBO;
GLuint EBO;
std::vector<GLuint> UnimLocs;
DrawLogicFunc DrawFunc = nullptr;
};
private:
SDL_Window *_window;
// 窗口尺寸
int _windowWidth;
int _windowHeight;
// 正交投影矩阵
glm::mat4 _orthoMatrix;
// 渲染器上下文
SDL_GLContext _ctx;
// 着色器程序
std::map<std::string, GLuint> _shaderProgramMap;
// 渲染参数
std::map<std::string, GL_RenderParams> _RenderParamsMap;
// 当前使用着色器程序
GLuint _currentShaderProgram;
// 当前使用渲染参数
GL_RenderParams _currentRenderParams;
public:
RenderManager(SDL_Window *window);
~RenderManager();
// 初始化着色器程序
void InitShaderProgram();
// 初始化绘制2D纹理缓冲程序
void Init2DTextureProgram();
// 设定当前使用的着色器程序
void SetCurrentShaderProgram(std::string name);
// 设定当前使用的缓冲对象
void SetCurrentBufferObject(std::string name);
// 清空屏幕
void ClearScreen();
// 交换缓冲区
void SwapBuffer();
// 设置裁切区域
void SetClipRect(const SDL_Rect *rect);
// 关闭裁切区域
void CloseClipRect();
// 绘制纹理
void DrawTexture(RefPtr<Texture> texture, const SDL_Rect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, void *userdata = nullptr);
private:
// 编译着色器
GLuint CompileShader(GLenum type, std::string Path);
// 新增缓冲程序
void AddBufferObject(std::string name, GL_RenderParams bufferObject);
};

View File

@@ -1,91 +1,204 @@
#include "EngineFrame/Render/Texture.h"
#include "Texture.h"
#include "Asset/Asset_ImagePack.h"
#include "EngineCore/Game.h"
#include "Texture.h"
Texture::Texture()
{
}
Texture::Texture(std::string PngPath)
Texture::~Texture()
{
glDeleteTextures(1, &m_TextureID);
}
bool Texture::Init(std::string PngPath)
{
SDL_Surface *surface = IMG_Load(PngPath.c_str());
if (!surface)
{
SDL_Log("无法加载图片: %s", SDL_GetError());
return;
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Couldn't load image %s: %s", PngPath.c_str(), IMG_GetError());
return false;
}
m_texture = SDL_CreateTextureFromSurface(Game::GetInstance().GetRenderer(), surface);
if (!m_texture)
{
SDL_Log("无法创建纹理: %s", SDL_GetError());
SDL_FreeSurface(surface); // 记得释放surface
return;
}
this->TextureSize.width = surface->w;
this->TextureSize.height = surface->h;
this->TextureFramepos.width = surface->w;
this->TextureFramepos.height = surface->h;
SDL_Surface *rgbaSurface = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ABGR8888, 0);
SDL_FreeSurface(surface);
if (!rgbaSurface)
{
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to convert surface: %s\n", SDL_GetError());
return false;
}
// 生成OpenGL纹理
glGenTextures(1, &m_TextureID);
glBindTexture(GL_TEXTURE_2D, m_TextureID);
// 环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 缩小过滤:使用最近邻采样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// 放大过滤:使用最近邻采样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// 处理像素对齐确保SDL的像素行对齐与OpenGL兼容
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// 记录纹理信息
m_TextureSize.width = rgbaSurface->w;
m_TextureSize.height = rgbaSurface->h;
m_FrameSize.width = rgbaSurface->w;
m_FrameSize.height = rgbaSurface->h;
m_Path = PngPath;
// 将像素数据上传到GPU
glTexImage2D(
GL_TEXTURE_2D,
0, // 纹理级别(基础级)
GL_RGBA8, // 内部存储格式8位RGBA
m_TextureSize.width,
m_TextureSize.height,
0, // 边框必须为0
GL_RGBA, // 源数据格式与SDL转换后的格式一致
GL_UNSIGNED_BYTE, // 源数据类型(无符号字节)
rgbaSurface->pixels);
// 释放转换后的表面数据已上传到GPU
SDL_FreeSurface(rgbaSurface);
return true;
}
Texture::Texture(std::string imgPath, int Index)
bool Texture::Init(std::string ImgPath, int Index)
{
this->ImgPath = imgPath;
this->Index = Index;
Asset_ImagePack::IMG *Info = Asset_ImagePack::GetInstance().GetIMG(imgPath);
m_Path = ImgPath;
m_TextureIndex = Index;
Asset_ImagePack::IMG *Info = Asset_ImagePack::GetInstance().GetIMG(ImgPath);
if (Info->lpImgName == "sprite/interface/base.img")
return;
return false;
Asset_ImagePack::ImgInfo &Buf = Info->lp_lplist[Index];
m_texture = SDL_CreateTexture(
Game::GetInstance().GetRenderer(),
SDL_PIXELFORMAT_ARGB8888, // 匹配RGBA数据格式
SDL_TEXTUREACCESS_STREAMING,
Buf.Width, Buf.Height);
if (!m_texture)
// 检查像素数据是否有效
if (!Buf.PNGdata || Buf.Width <= 0 || Buf.Height <= 0)
{
SDL_Log("纹理创建失败: %s", SDL_GetError());
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "无效的图像数据: %s (index: %d)", ImgPath.c_str(), Index);
return false;
}
int pitch = Buf.Width * 4;
SDL_UpdateTexture(m_texture, NULL, Buf.PNGdata, pitch);
SDL_SetTextureBlendMode(m_texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureScaleMode(m_texture, SDL_ScaleModeNearest);
this->TexturePos.x = Buf.Xpos;
this->TexturePos.y = Buf.Ypos;
this->TextureSize.width = Buf.Width;
this->TextureSize.height = Buf.Height;
this->TextureFramepos.width = Buf.FrameXpos;
this->TextureFramepos.height = Buf.FrameYpos;
int pixelCount = Buf.Width * Buf.Height;
uint32_t *srcPixels = reinterpret_cast<uint32_t *>(Buf.PNGdata);
uint32_t *dstPixels = new uint32_t[pixelCount];
for (int i = 0; i < pixelCount; ++i)
{
uint8_t b = (srcPixels[i] >> 0) & 0xFF; // 蓝
uint8_t g = (srcPixels[i] >> 8) & 0xFF; // 绿
uint8_t r = (srcPixels[i] >> 16) & 0xFF; // 红
uint8_t a = (srcPixels[i] >> 24) & 0xFF; // 透明
dstPixels[i] = (a << 24) | (b << 16) | (g << 8) | r; // 重组为 RGBA
}
// 生成OpenGL纹理
glGenTextures(1, &m_TextureID);
glBindTexture(GL_TEXTURE_2D, m_TextureID);
// 设置纹理参数保持与原有SDL纹理相同的采样模式
// 环绕方式:重复
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 过滤方式最近邻与SDL_ScaleModeNearest对应
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 处理像素对齐SDL表面可能有不同的行对齐方式
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// 记录纹理信息
m_TextureSize.width = Buf.Width;
m_TextureSize.height = Buf.Height;
m_FrameSize.width = Buf.FrameXpos;
m_FrameSize.height = Buf.FrameYpos;
m_TexturePos.x = Buf.Xpos;
m_TexturePos.y = Buf.Ypos;
// 上传纹理数据到GPU
glTexImage2D(
GL_TEXTURE_2D,
0, // 基础纹理级别
GL_RGBA8, // 内部存储格式8位RGBA
m_TextureSize.width,
m_TextureSize.height,
0, // 无边框
GL_RGBA, // 源数据格式(根据实际数据格式调整)
GL_UNSIGNED_BYTE, // 源数据类型
dstPixels);
return true;
}
Texture::~Texture()
bool Texture::Init(SDL_Surface *rgbaSurface)
{
SDL_DestroyTexture(m_texture);
// 生成OpenGL纹理
glGenTextures(1, &m_TextureID);
glBindTexture(GL_TEXTURE_2D, m_TextureID);
// 环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 缩小过滤:使用最近邻采样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// 放大过滤:使用最近邻采样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 处理像素对齐确保SDL的像素行对齐与OpenGL兼容
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// 记录纹理信息
m_TextureSize.width = rgbaSurface->w;
m_TextureSize.height = rgbaSurface->h;
m_FrameSize.width = rgbaSurface->w;
m_FrameSize.height = rgbaSurface->h;
// 将像素数据上传到GPU
glTexImage2D(
GL_TEXTURE_2D,
0, // 纹理级别(基础级)
GL_RGBA8, // 内部存储格式8位RGBA
m_TextureSize.width,
m_TextureSize.height,
0, // 边框必须为0
GL_RGBA, // 源数据格式与SDL转换后的格式一致
GL_UNSIGNED_BYTE, // 源数据类型(无符号字节)
rgbaSurface->pixels);
return true;
}
SDL_Texture *Texture::GetTexture()
bool Texture::Init(VecSize size)
{
return m_texture;
}
glGenTextures(1, &m_TextureID);
glBindTexture(GL_TEXTURE_2D, m_TextureID);
// 设置纹理参数(根据需求调整尺寸、格式)
glTexImage2D(
GL_TEXTURE_2D,
0, // 多级渐远纹理级别
GL_RGBA, // 内部格式(存储格式)
size.width, // 纹理宽度
size.height, // 纹理高度
0, // 边框必须为0
GL_RGBA, // 源数据格式
GL_UNSIGNED_BYTE, // 源数据类型
nullptr // 初始数据(为空,后续绘制填充)
);
RefPtr<Texture> Texture::GetTextureCopy()
{
RefPtr<Texture> nt = new Texture(this->ImgPath, this->Index);
return nt;
}
// 记录纹理信息
m_TextureSize = size;
m_FrameSize = size;
void Texture::SetBlendMode(SDL_BlendMode blendMode)
{
SDL_SetTextureBlendMode(m_texture, blendMode);
}
// 环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 缩小过滤:使用最近邻采样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// 放大过滤:使用最近邻采样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
SDL_BlendMode Texture::GetBlendMode()
{
SDL_BlendMode blendMode;
SDL_GetTextureBlendMode(m_texture, &blendMode);
return blendMode;
}
glBindTexture(GL_TEXTURE_2D, 0); // 解绑
return true;
}

View File

@@ -1,33 +1,59 @@
#pragma once
#include <SDL.h>
#include <SDL_image.h>
#include <glad/glad.h>
#include <string>
#include "Tool/RefPtr.h"
#include "Tool/Common.h"
#include "Tool/RefObject.h"
#include "Tool/RefPtr.h"
#include "Tool/IntrusiveList.hpp"
class Texture : public RefObject
{
private:
SDL_Texture *m_texture = nullptr;
std::string ImgPath = "";
int Index = 0;
public:
VecSize TextureSize = {0, 0}; // 纹理大小
VecPos TexturePos = {0, 0}; // 纹理位置
VecSize TextureFramepos = {0, 0}; // 帧域宽高
// OpenGL 纹理ID
GLuint m_TextureID;
// 纹理大小
VecSize m_TextureSize;
// 帧域大小
VecSize m_FrameSize;
// 纹理坐标
VecPos m_TexturePos;
// 纹理透明度
float m_Alpha = 1.0f;
// 纹理路径
std::string m_Path;
// 纹理编号(可选 在使用img构造时才会有编号)
int m_TextureIndex = -1;
public:
Texture(/* args */);
Texture(std::string PngPath);
Texture(std::string imgPath, int Index);
~Texture();
Texture(const Texture &) = delete;
Texture &operator=(const Texture &) = delete;
public:
void SetBlendMode(SDL_BlendMode blendMode);
// 获取混合模式
SDL_BlendMode GetBlendMode();
// 获取纹理
SDL_Texture *GetTexture();
// 获取一个拷贝的纹理
RefPtr<Texture> GetTextureCopy();
// 获取纹理ID
GLuint getID() const { return m_TextureID; }
// 设置纹理透明度
void setAlpha(float alpha) { m_Alpha = alpha; }
// 获取纹理透明度
float getAlpha() const { return m_Alpha; }
// 获取纹理大小
VecSize getSize() const { return m_TextureSize; }
// 获取纹理坐标
VecPos getPos() const { return m_TexturePos; }
// 获取纹理路径
std::string getPath() const { return m_Path; }
// 获取纹理编号
int getIndex() const { return m_TextureIndex; }
// 获取帧域大小
VecSize getFrameSize() const { return m_FrameSize; }
public:
bool Init(std::string PngPath);
bool Init(std::string ImgPath, int index);
bool Init(SDL_Surface *surface);
bool Init(VecSize size);
};