2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-05-14 01:51:22 +00:00
metaforce/Runtime/GuiSys/CTextRenderBuffer.cpp
Lioncash 20b74c4818 CTextRenderBuffer: Forward declare structs where applicable
These are only used internally, so we can completely hide these structs
from external view.

This allows their implementation to be changed without needing to
recompile all the headers that include it.
2020-03-29 03:54:29 -04:00

282 lines
11 KiB
C++

#include "Runtime/GuiSys/CTextRenderBuffer.hpp"
#include "Runtime/Graphics/CGraphics.hpp"
#include "Runtime/Graphics/CGraphicsPalette.hpp"
#include "Runtime/Graphics/CTexture.hpp"
#include "Runtime/Graphics/Shaders/CTextSupportShader.hpp"
#include "Runtime/GuiSys/CFontImageDef.hpp"
#include "Runtime/GuiSys/CFontRenderState.hpp"
#include "Runtime/GuiSys/CInstruction.hpp"
#include "Runtime/GuiSys/CRasterFont.hpp"
#include "Runtime/GuiSys/CTextExecuteBuffer.hpp"
namespace urde {
struct CTextRenderBuffer::BooFontCharacters {
TLockedToken<CRasterFont> m_font;
hecl::VertexBufferPool<CTextSupportShader::CharacterInstance>::Token m_instBuf;
boo::ObjToken<boo::IShaderDataBinding> m_dataBinding;
boo::ObjToken<boo::IShaderDataBinding> m_dataBinding2;
std::vector<CTextSupportShader::CharacterInstance> m_charData;
u32 m_charCount = 0;
bool m_dirty = true;
BooFontCharacters(const CToken& token) : m_font(token) {}
};
struct CTextRenderBuffer::BooImage {
CFontImageDef m_imageDef;
hecl::VertexBufferPool<CTextSupportShader::ImageInstance>::Token m_instBuf;
std::vector<boo::ObjToken<boo::IShaderDataBinding>> m_dataBinding;
std::vector<boo::ObjToken<boo::IShaderDataBinding>> m_dataBinding2;
CTextSupportShader::ImageInstance m_imageData;
bool m_dirty = true;
BooImage(const CFontImageDef& imgDef, const zeus::CVector2i& offset) : m_imageDef(imgDef) {
m_imageData.SetMetrics(imgDef, offset);
}
};
struct CTextRenderBuffer::BooPrimitiveMark {
Command m_cmd;
u32 m_bindIdx;
u32 m_instIdx;
void SetOpacity(CTextRenderBuffer& rb, float opacity) {
switch (m_cmd) {
case Command::CharacterRender: {
BooFontCharacters& fc = rb.m_fontCharacters[m_bindIdx];
CTextSupportShader::CharacterInstance& inst = fc.m_charData[m_instIdx];
inst.m_mulColor.a() = opacity;
fc.m_dirty = true;
break;
}
case Command::ImageRender: {
BooImage& img = rb.m_images[m_bindIdx];
img.m_imageData.m_color.a() = opacity;
img.m_dirty = true;
break;
}
default:
break;
}
}
};
CTextRenderBuffer::CTextRenderBuffer(EMode mode, CGuiWidget::EGuiModelDrawFlags df) : x0_mode(mode), m_drawFlags(df) {}
CTextRenderBuffer::~CTextRenderBuffer() = default;
void CTextRenderBuffer::CommitResources() {
if (m_committed)
return;
m_committed = true;
/* Ensure font textures are ready outside transaction */
for (BooFontCharacters& chs : m_fontCharacters)
chs.m_font->GetTexture();
CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) {
m_uniBuf = CTextSupportShader::s_Uniforms.allocateBlock(CGraphics::g_BooFactory);
auto uBufInfo = m_uniBuf.getBufferInfo();
decltype(uBufInfo) uBufInfo2;
if (m_drawFlags == CGuiWidget::EGuiModelDrawFlags::AlphaAdditiveOverdraw) {
m_uniBuf2 = CTextSupportShader::s_Uniforms.allocateBlock(CGraphics::g_BooFactory);
uBufInfo2 = m_uniBuf2.getBufferInfo();
}
for (BooFontCharacters& chs : m_fontCharacters) {
chs.m_instBuf = CTextSupportShader::s_CharInsts.allocateBlock(CGraphics::g_BooFactory, chs.m_charCount);
auto iBufInfo = chs.m_instBuf.getBufferInfo();
boo::ObjToken<boo::IGraphicsBuffer> uniforms[] = {uBufInfo.first.get()};
boo::PipelineStage unistages[] = {boo::PipelineStage::Vertex};
size_t unioffs[] = {size_t(uBufInfo.second)};
size_t unisizes[] = {sizeof(CTextSupportShader::Uniform)};
boo::ObjToken<boo::ITexture> texs[] = {chs.m_font->GetTexture()};
chs.m_dataBinding = ctx.newShaderDataBinding(CTextSupportShader::SelectTextPipeline(m_drawFlags), nullptr,
iBufInfo.first.get(), nullptr, 1, uniforms, unistages, unioffs,
unisizes, 1, texs, nullptr, nullptr, 0, iBufInfo.second);
if (m_drawFlags == CGuiWidget::EGuiModelDrawFlags::AlphaAdditiveOverdraw) {
uniforms[0] = uBufInfo2.first.get();
unioffs[0] = size_t(uBufInfo2.second);
chs.m_dataBinding2 = ctx.newShaderDataBinding(CTextSupportShader::GetTextAdditiveOverdrawPipeline(), nullptr,
iBufInfo.first.get(), nullptr, 1, uniforms, unistages, unioffs,
unisizes, 1, texs, nullptr, nullptr, 0, iBufInfo.second);
}
}
for (BooImage& img : m_images) {
img.m_instBuf = CTextSupportShader::s_ImgInsts.allocateBlock(CGraphics::g_BooFactory, 1);
auto iBufInfo = img.m_instBuf.getBufferInfo();
boo::ObjToken<boo::IGraphicsBuffer> uniforms[] = {uBufInfo.first.get()};
boo::PipelineStage unistages[] = {boo::PipelineStage::Vertex};
size_t unioffs[] = {size_t(uBufInfo.second)};
size_t unisizes[] = {sizeof(CTextSupportShader::Uniform)};
img.m_dataBinding.reserve(img.m_imageDef.x4_texs.size());
for (TToken<CTexture>& tex : img.m_imageDef.x4_texs) {
boo::ObjToken<boo::ITexture> texs[] = {tex->GetBooTexture()};
texs[0]->setClampMode(boo::TextureClampMode::ClampToEdge);
img.m_dataBinding.push_back(ctx.newShaderDataBinding(
CTextSupportShader::SelectImagePipeline(m_drawFlags), nullptr, iBufInfo.first.get(), nullptr, 1, uniforms,
unistages, unioffs, unisizes, 1, texs, nullptr, nullptr, 0, iBufInfo.second));
}
if (m_drawFlags == CGuiWidget::EGuiModelDrawFlags::AlphaAdditiveOverdraw) {
uniforms[0] = uBufInfo2.first.get();
unioffs[0] = size_t(uBufInfo2.second);
img.m_dataBinding2.reserve(img.m_imageDef.x4_texs.size());
for (TToken<CTexture>& tex : img.m_imageDef.x4_texs) {
boo::ObjToken<boo::ITexture> texs[] = {tex->GetBooTexture()};
img.m_dataBinding2.push_back(ctx.newShaderDataBinding(
CTextSupportShader::GetImageAdditiveOverdrawPipeline(), nullptr, iBufInfo.first.get(), nullptr, 1,
uniforms, unistages, unioffs, unisizes, 1, texs, nullptr, nullptr, 0, iBufInfo.second));
}
}
}
return true;
} BooTrace);
}
void CTextRenderBuffer::SetMode(EMode mode) {
if (mode == EMode::BufferFill) {
m_images.reserve(m_imagesCount);
for (BooFontCharacters& fc : m_fontCharacters)
fc.m_charData.reserve(fc.m_charCount);
}
m_activeFontCh = -1;
x0_mode = mode;
}
void CTextRenderBuffer::SetPrimitiveOpacity(int idx, float opacity) {
m_primitiveMarks[idx].SetOpacity(*this, opacity);
}
u32 CTextRenderBuffer::GetPrimitiveCount() const { return m_primitiveMarks.size(); }
void CTextRenderBuffer::Render(const zeus::CColor& col, float time) const {
const_cast<CTextRenderBuffer*>(this)->CommitResources();
zeus::CMatrix4f mv = CGraphics::g_GXModelView.toMatrix4f();
zeus::CMatrix4f proj = CGraphics::GetPerspectiveProjectionMatrix(true);
zeus::CMatrix4f mat = proj * mv;
const_cast<CTextRenderBuffer*>(this)->m_uniBuf.access() = CTextSupportShader::Uniform{mat, col};
if (m_drawFlags == CGuiWidget::EGuiModelDrawFlags::AlphaAdditiveOverdraw) {
zeus::CColor colPremul = col * col.a();
colPremul.a() = col.a();
const_cast<CTextRenderBuffer*>(this)->m_uniBuf2.access() = CTextSupportShader::Uniform{mat, colPremul};
}
for (const BooFontCharacters& chs : m_fontCharacters) {
if (chs.m_charData.size()) {
if (chs.m_dirty) {
memmove(const_cast<BooFontCharacters&>(chs).m_instBuf.access(), chs.m_charData.data(),
sizeof(CTextSupportShader::CharacterInstance) * chs.m_charData.size());
const_cast<BooFontCharacters&>(chs).m_dirty = false;
}
CGraphics::SetShaderDataBinding(chs.m_dataBinding);
CGraphics::DrawInstances(0, 4, chs.m_charData.size());
if (m_drawFlags == CGuiWidget::EGuiModelDrawFlags::AlphaAdditiveOverdraw) {
CGraphics::SetShaderDataBinding(chs.m_dataBinding2);
CGraphics::DrawInstances(0, 4, chs.m_charData.size());
}
}
}
for (const BooImage& img : m_images) {
if (img.m_dirty) {
*const_cast<BooImage&>(img).m_instBuf.access() = img.m_imageData;
const_cast<BooImage&>(img).m_dirty = false;
}
int idx = int(img.m_imageDef.x0_fps * time) % img.m_dataBinding.size();
CGraphics::SetShaderDataBinding(img.m_dataBinding[idx]);
CGraphics::DrawInstances(0, 4, 1);
if (m_drawFlags == CGuiWidget::EGuiModelDrawFlags::AlphaAdditiveOverdraw) {
CGraphics::SetShaderDataBinding(img.m_dataBinding2[idx]);
CGraphics::DrawInstances(0, 4, 1);
}
}
}
void CTextRenderBuffer::AddImage(const zeus::CVector2i& offset, const CFontImageDef& image) {
if (x0_mode == EMode::AllocTally)
m_primitiveMarks.push_back({Command::ImageRender, m_imagesCount++, 0});
else
m_images.emplace_back(image, offset);
}
void CTextRenderBuffer::AddCharacter(const zeus::CVector2i& offset, char16_t ch, const zeus::CColor& color) {
if (m_activeFontCh == UINT32_MAX)
return;
BooFontCharacters& chs = m_fontCharacters[m_activeFontCh];
if (x0_mode == EMode::AllocTally)
m_primitiveMarks.push_back({Command::CharacterRender, m_activeFontCh, chs.m_charCount++});
else {
const CGlyph* glyph = chs.m_font.GetObj()->GetGlyph(ch);
CTextSupportShader::CharacterInstance& inst = chs.m_charData.emplace_back();
inst.SetMetrics(*glyph, offset);
inst.m_fontColor = m_main * color;
inst.m_outlineColor = m_outline * color;
inst.m_mulColor = zeus::skWhite;
}
}
void CTextRenderBuffer::AddPaletteChange(const zeus::CColor& main, const zeus::CColor& outline) {
m_main = main;
m_outline = outline;
}
void CTextRenderBuffer::AddFontChange(const TToken<CRasterFont>& font) {
for (size_t i = 0; i < m_fontCharacters.size(); ++i) {
BooFontCharacters& chs = m_fontCharacters[i];
if (*chs.m_font.GetObjectTag() == *font.GetObjectTag()) {
m_activeFontCh = i;
return;
}
}
m_activeFontCh = m_fontCharacters.size();
m_fontCharacters.emplace_back(font);
}
bool CTextRenderBuffer::HasSpaceAvailable(const zeus::CVector2i& origin, const zeus::CVector2i& extent) const {
std::pair<zeus::CVector2i, zeus::CVector2i> bounds = AccumulateTextBounds();
if (bounds.first.x > bounds.second.x)
return true;
if (0 < origin.y)
return false;
zeus::CVector2i size = bounds.second - bounds.first;
return size.y <= extent.y;
}
std::pair<zeus::CVector2i, zeus::CVector2i> CTextRenderBuffer::AccumulateTextBounds() const {
std::pair<zeus::CVector2i, zeus::CVector2i> ret =
std::make_pair(zeus::CVector2i{INT_MAX, INT_MAX}, zeus::CVector2i{INT_MIN, INT_MIN});
for (const BooFontCharacters& chars : m_fontCharacters) {
for (const CTextSupportShader::CharacterInstance& charInst : chars.m_charData) {
ret.first.x = std::min(ret.first.x, int(charInst.m_pos[0].x()));
ret.first.y = std::min(ret.first.y, int(charInst.m_pos[0].z()));
ret.second.x = std::max(ret.second.x, int(charInst.m_pos[3].x()));
ret.second.y = std::max(ret.second.y, int(charInst.m_pos[3].z()));
}
}
for (const BooImage& imgs : m_images) {
ret.first.x = std::min(ret.first.x, int(imgs.m_imageData.m_pos[0].x()));
ret.first.y = std::min(ret.first.y, int(imgs.m_imageData.m_pos[0].z()));
ret.second.x = std::max(ret.second.x, int(imgs.m_imageData.m_pos[3].x()));
ret.second.y = std::max(ret.second.y, int(imgs.m_imageData.m_pos[3].z()));
}
return ret;
}
} // namespace urde