2023-01-02 13:17:18 -06:00
|
|
|
#include "gx/font/CGxStringBatch.hpp"
|
|
|
|
|
#include "gx/font/CGxFont.hpp"
|
|
|
|
|
#include "gx/Buffer.hpp"
|
|
|
|
|
#include "gx/CGxBatch.hpp"
|
|
|
|
|
#include "gx/Device.hpp"
|
|
|
|
|
#include "gx/Draw.hpp"
|
|
|
|
|
#include "gx/Font.hpp"
|
|
|
|
|
#include "gx/Gx.hpp"
|
|
|
|
|
#include "gx/RenderState.hpp"
|
|
|
|
|
#include "gx/Shader.hpp"
|
|
|
|
|
#include "gx/Texture.hpp"
|
|
|
|
|
#include "gx/Transform.hpp"
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <storm/Error.hpp>
|
|
|
|
|
|
|
|
|
|
bool BATCHEDRENDERFONTDESC::s_billboarded;
|
|
|
|
|
CGxBuf* BATCHEDRENDERFONTDESC::s_indexBuf;
|
|
|
|
|
CGxPool* BATCHEDRENDERFONTDESC::s_indexPool;
|
|
|
|
|
|
|
|
|
|
int32_t SetProjection() {
|
|
|
|
|
float minX, maxX, minY, maxY, minZ, maxZ;
|
|
|
|
|
GxXformViewport(minX, maxX, minY, maxY, minZ, maxZ);
|
|
|
|
|
|
|
|
|
|
float v14 = 0.0f;
|
|
|
|
|
float v15 = 0.0f;
|
|
|
|
|
|
2023-03-09 22:29:33 -06:00
|
|
|
if (!GxCaps().m_pixelCenterOnEdge) {
|
2023-01-02 13:17:18 -06:00
|
|
|
v14 = -0.5f;
|
|
|
|
|
v15 = 0.5f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C44Matrix proj;
|
|
|
|
|
|
|
|
|
|
uint32_t pixelWidth = GetScreenPixelWidth();
|
|
|
|
|
float pixelMinX = floor(minX * pixelWidth);
|
|
|
|
|
float pixelMaxX = floor(maxX * pixelWidth);
|
|
|
|
|
|
|
|
|
|
if (pixelMinX >= pixelMaxX) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t pixelHeight = GetScreenPixelHeight();
|
|
|
|
|
float pixelMinY = floor(minY * pixelHeight);
|
|
|
|
|
float pixelMaxY = floor(maxY * pixelHeight);
|
|
|
|
|
|
|
|
|
|
if (pixelMinY >= pixelMaxY) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pixelHeight > pixelMaxY) {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pixelMinX += v14;
|
|
|
|
|
pixelMaxX += v14;
|
|
|
|
|
pixelMinY += v15;
|
|
|
|
|
pixelMaxY += v15;
|
|
|
|
|
|
|
|
|
|
GxuXformCreateOrthoDepth(pixelMinX, pixelMaxX, pixelMinY, pixelMaxY, -5000.0f, 5000.0f, proj);
|
|
|
|
|
GxXformSetProjection(proj);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BATCHEDRENDERFONTDESC::Initialize() {
|
|
|
|
|
CGxPool* indexPool = GxPoolCreate(
|
|
|
|
|
GxPoolTarget_Index,
|
|
|
|
|
GxPoolUsage_Static,
|
|
|
|
|
6144,
|
|
|
|
|
GxPoolHintBit_Unk0,
|
|
|
|
|
"BATCHEDRENDERFONTDESC_idx"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CGxBuf* indexBuf = GxBufCreate(
|
|
|
|
|
indexPool,
|
|
|
|
|
2,
|
|
|
|
|
3072,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
BATCHEDRENDERFONTDESC::s_indexPool = indexPool;
|
|
|
|
|
BATCHEDRENDERFONTDESC::s_indexBuf = indexBuf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BATCHEDRENDERFONTDESC::InitializeIndexBuff() {
|
|
|
|
|
char* indexData = g_theGxDevicePtr->BufLock(BATCHEDRENDERFONTDESC::s_indexBuf);
|
|
|
|
|
uint16_t* indexBuf = reinterpret_cast<uint16_t*>(indexData);
|
|
|
|
|
|
|
|
|
|
uint16_t index = 0;
|
|
|
|
|
|
|
|
|
|
for (int32_t i = 0; i < 512; i++) {
|
|
|
|
|
indexBuf[i * 6 + 0] = index + 0;
|
|
|
|
|
indexBuf[i * 6 + 1] = index + 2;
|
|
|
|
|
indexBuf[i * 6 + 2] = index + 1;
|
|
|
|
|
indexBuf[i * 6 + 3] = index + 2;
|
|
|
|
|
indexBuf[i * 6 + 4] = index + 3;
|
|
|
|
|
indexBuf[i * 6 + 5] = index + 1;
|
|
|
|
|
|
|
|
|
|
index += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GxBufUnlock(BATCHEDRENDERFONTDESC::s_indexBuf, 0);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CGxVertexPCT* BATCHEDRENDERFONTDESC::UnlockVertexPtrAndRender(CGxBuf*& buf, int32_t count) {
|
|
|
|
|
GxBufUnlock(buf, sizeof(CGxVertexPCT) * count);
|
|
|
|
|
|
|
|
|
|
if (!BATCHEDRENDERFONTDESC::s_indexBuf->unk1C || !BATCHEDRENDERFONTDESC::s_indexBuf->unk1D) {
|
|
|
|
|
BATCHEDRENDERFONTDESC::InitializeIndexBuff();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BATCHEDRENDERFONTDESC::s_indexBuf->unk1C && BATCHEDRENDERFONTDESC::s_indexBuf->unk1D) {
|
|
|
|
|
GxPrimVertexPtr(buf, GxVBF_PCT);
|
|
|
|
|
GxPrimIndexPtr(BATCHEDRENDERFONTDESC::s_indexBuf);
|
|
|
|
|
|
|
|
|
|
CGxBatch batch;
|
|
|
|
|
batch.m_primType = GxPrim_Triangles;
|
|
|
|
|
batch.m_start = 0;
|
|
|
|
|
batch.m_count = 6 * (count / 4);
|
|
|
|
|
batch.m_minIndex = 0;
|
|
|
|
|
batch.m_maxIndex = count - 1;
|
|
|
|
|
|
|
|
|
|
GxDraw(&batch, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return reinterpret_cast<CGxVertexPCT*>(g_theGxDevicePtr->BufLock(buf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BATCHEDRENDERFONTDESC::RenderBatch() {
|
|
|
|
|
if (!BATCHEDRENDERFONTDESC::s_indexPool) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto string = this->m_strings.Head(); string; string = this->m_strings.Next(string)) {
|
|
|
|
|
string->CheckGeometry();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int32_t i = 0; i < 8; i++) {
|
|
|
|
|
this->m_face->m_textureCache[i].UpdateDirty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t maxBatchCapacity = 2048;
|
|
|
|
|
|
|
|
|
|
CGxBuf* vertexStream = g_theGxDevicePtr->BufStream(GxPoolTarget_Vertex, 0x18, maxBatchCapacity);
|
|
|
|
|
char* vertexData = g_theGxDevicePtr->BufLock(vertexStream);
|
|
|
|
|
CGxVertexPCT* vertexBuf = reinterpret_cast<CGxVertexPCT*>(vertexData);
|
|
|
|
|
|
|
|
|
|
for (int32_t i = 0; i < 8; i++) {
|
|
|
|
|
auto& textureCache = this->m_face->m_textureCache[i];
|
|
|
|
|
auto texture = textureCache.m_texture;
|
|
|
|
|
|
|
|
|
|
if (texture) {
|
|
|
|
|
auto gxTex = TextureGetGxTex(reinterpret_cast<CTexture*>(texture), 1, nullptr);
|
|
|
|
|
|
|
|
|
|
if (gxTex) {
|
|
|
|
|
GxRsSet(GxRs_Texture0, gxTex);
|
|
|
|
|
|
|
|
|
|
for (auto string = this->m_strings.Head(); string; string = this->m_strings.Next(string)) {
|
|
|
|
|
auto line = string->m_textLines[i];
|
|
|
|
|
|
|
|
|
|
if (line) {
|
|
|
|
|
int32_t vertsNeeded = string->CalculateVertsNeeded(i);
|
|
|
|
|
int32_t batchOffset = 0;
|
|
|
|
|
int32_t batchCapacity = maxBatchCapacity;
|
|
|
|
|
|
|
|
|
|
while (vertsNeeded) {
|
|
|
|
|
int32_t batchCount = std::min(vertsNeeded, batchCapacity);
|
|
|
|
|
|
|
|
|
|
string->WriteGeometry(vertexBuf, i, batchOffset, batchCount);
|
|
|
|
|
|
|
|
|
|
vertsNeeded -= batchCount;
|
|
|
|
|
batchOffset += batchCount;
|
|
|
|
|
batchCapacity -= batchCount;
|
|
|
|
|
vertexBuf += batchCount;
|
|
|
|
|
|
|
|
|
|
if (!batchCapacity) {
|
|
|
|
|
vertexBuf = this->UnlockVertexPtrAndRender(vertexStream, maxBatchCapacity);
|
|
|
|
|
batchCapacity = maxBatchCapacity;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (batchCapacity != maxBatchCapacity) {
|
|
|
|
|
vertexBuf = this->UnlockVertexPtrAndRender(vertexStream, maxBatchCapacity - batchCapacity);
|
|
|
|
|
batchCapacity = maxBatchCapacity;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_theGxDevicePtr->BufUnlock(vertexStream, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CGxStringBatch::~CGxStringBatch() {
|
|
|
|
|
this->m_fontBatch.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CGxStringBatch::AddString(CGxString* string) {
|
|
|
|
|
STORM_ASSERT(string);
|
|
|
|
|
STORM_ASSERT(string->m_currentFace);
|
|
|
|
|
|
|
|
|
|
auto face = string->m_currentFace;
|
|
|
|
|
uint32_t hashval = reinterpret_cast<uintptr_t>(face);
|
|
|
|
|
HASHKEY_PTR key = { face };
|
|
|
|
|
|
|
|
|
|
auto batch = this->m_fontBatch.Ptr(hashval, key);
|
|
|
|
|
|
|
|
|
|
if (!batch) {
|
|
|
|
|
batch = this->m_fontBatch.New(hashval, key, 0, 0);
|
|
|
|
|
batch->m_face = face;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
batch->m_strings.LinkToTail(string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CGxStringBatch::RenderBatch() {
|
|
|
|
|
// TODO
|
|
|
|
|
// if (!g_perf) {
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
C44Matrix oldProjection;
|
|
|
|
|
C44Matrix oldView;
|
|
|
|
|
|
|
|
|
|
GxXformProjection(oldProjection);
|
|
|
|
|
GxXformView(oldView);
|
|
|
|
|
|
|
|
|
|
GxRsPush();
|
|
|
|
|
|
|
|
|
|
GxRsSet(GxRs_Fog, 0);
|
|
|
|
|
GxRsSet(GxRs_Culling, 0);
|
|
|
|
|
GxRsSet(GxRs_BlendingMode, GxBlend_Alpha);
|
|
|
|
|
GxRsSetAlphaRef();
|
|
|
|
|
|
|
|
|
|
int32_t setProjection;
|
|
|
|
|
int32_t stereoEnabled;
|
|
|
|
|
|
|
|
|
|
if (this->m_flags & 0x1) {
|
|
|
|
|
BATCHEDRENDERFONTDESC::s_billboarded = true;
|
|
|
|
|
|
|
|
|
|
GxRsSet(GxRs_DepthTest, 1);
|
|
|
|
|
GxRsSet(GxRs_DepthWrite, 1);
|
|
|
|
|
GxRsSet(GxRs_AlphaRef, 1);
|
|
|
|
|
|
|
|
|
|
setProjection = 1;
|
|
|
|
|
stereoEnabled = 0;
|
|
|
|
|
} else {
|
|
|
|
|
C44Matrix view;
|
|
|
|
|
GxXformSetView(view);
|
|
|
|
|
|
|
|
|
|
GxRsSet(GxRs_DepthTest, 0);
|
|
|
|
|
GxRsSet(GxRs_DepthWrite, 0);
|
|
|
|
|
|
|
|
|
|
setProjection = SetProjection();
|
|
|
|
|
stereoEnabled = g_theGxDevicePtr->StereoEnabled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CGxShader* vs = g_fontVertexShader[stereoEnabled ? 1 : 0];
|
|
|
|
|
CGxShader* ps = g_fontPixelShader[0];
|
|
|
|
|
|
|
|
|
|
if (setProjection && vs->Valid() && ps->Valid()) {
|
|
|
|
|
GxRsSet(GxRs_VertexShader, vs);
|
|
|
|
|
GxRsSet(GxRs_PixelShader, ps);
|
|
|
|
|
|
|
|
|
|
C44Matrix viewProjMat;
|
|
|
|
|
GxXformViewProjNativeTranspose(viewProjMat);
|
|
|
|
|
GxShaderConstantsSet(GxSh_Vertex, 0, reinterpret_cast<float*>(&viewProjMat), 4);
|
|
|
|
|
|
|
|
|
|
for (auto fontBatch = this->m_fontBatch.Head(); fontBatch; fontBatch = this->m_fontBatch.Next(fontBatch)) {
|
|
|
|
|
if (fontBatch->m_strings.Head()) {
|
|
|
|
|
fontBatch->RenderBatch();
|
|
|
|
|
|
|
|
|
|
if (this->m_flags & 0x2) {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this->m_fontBatch.Unlink(fontBatch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BATCHEDRENDERFONTDESC::s_billboarded = false;
|
|
|
|
|
|
|
|
|
|
GxRsPop();
|
|
|
|
|
|
|
|
|
|
GxXformSetView(oldView);
|
|
|
|
|
GxXformSetProjection(oldProjection);
|
|
|
|
|
}
|