thunderbrew/src/ui/CSimpleTexture.cpp
2023-01-02 13:17:18 -06:00

538 lines
15 KiB
C++

#include "ui/CSimpleTexture.hpp"
#include "gx/Coordinate.hpp"
#include "gx/Device.hpp"
#include "gx/Shader.hpp"
#include "ui/CFramePoint.hpp"
#include "ui/CRenderBatch.hpp"
#include "ui/CSimpleFrame.hpp"
#include "ui/CSimpleTextureScript.hpp"
#include "ui/FrameXML.hpp"
#include "ui/LoadXML.hpp"
#include "ui/Util.hpp"
#include "util/StringTo.hpp"
#include <algorithm>
#include <climits>
#include <common/XML.hpp>
#include <storm/String.hpp>
CGxShader* CSimpleTexture::s_imageModePixelShaders[2] = {};
uint16_t CSimpleTexture::s_indices[] = {
0, 1, 2,
2, 1, 3
};
int32_t CSimpleTexture::s_metatable;
int32_t CSimpleTexture::s_objectType;
EGxTexFilter CSimpleTexture::s_textureFilterMode = GxTex_Linear;
void CSimpleTexture::CreateScriptMetaTable() {
lua_State* L = FrameScript_GetContext();
int32_t ref = FrameScript_Object::CreateScriptMetaTable(L, &CSimpleTexture::RegisterScriptMethods);
CSimpleTexture::s_metatable = ref;
}
CGxShader* CSimpleTexture::GetImageModePixelShader(TextureImageMode mode) {
CGxShader* shader = CSimpleTexture::s_imageModePixelShaders[mode];
if (shader && shader->Valid()) {
return shader;
} else {
return nullptr;
}
}
int32_t CSimpleTexture::GetObjectType() {
if (!CSimpleTexture::s_objectType) {
CSimpleTexture::s_objectType = ++FrameScript_Object::s_objectTypes;
}
return CSimpleTexture::s_objectType;
}
void CSimpleTexture::Init() {
static const char* paths[] = { "UI", "Desaturate" };
for (int32_t i = 0; i < 2; i++) {
g_theGxDevicePtr->ShaderCreate(
&s_imageModePixelShaders[i],
GxSh_Pixel,
"Shaders\\Pixel",
paths[i],
1
);
}
}
void CSimpleTexture::RegisterScriptMethods(lua_State* L) {
CScriptRegion::RegisterScriptMethods(L);
FrameScript_Object::FillScriptMethodTable(L, SimpleTextureMethods, NUM_SIMPLE_TEXTURE_SCRIPT_METHODS);
}
CSimpleTexture::CSimpleTexture(CSimpleFrame* frame, uint32_t drawlayer, int32_t show) : CSimpleRegion(frame, drawlayer, show) {
this->m_nonBlocking = 0;
this->m_updateTexCoord = 0;
this->m_horizTile = 0;
this->m_vertTile = 0;
this->m_shader = CSimpleTexture::GetImageModePixelShader(ImageMode_UI);
// TODO
// CSimpleTexture::s_count++;
this->m_texCoord[0] = { 0.0f, 0.0f };
this->m_texCoord[1] = { 0.0f, 1.0f };
this->m_texCoord[2] = { 1.0f, 0.0f };
this->m_texCoord[3] = { 1.0f, 1.0f };
}
CSimpleTexture::~CSimpleTexture() {
if (this->m_texture) {
HandleClose(this->m_texture);
}
// TODO CSimpleTexture::s_count++;
}
void CSimpleTexture::Draw(CRenderBatch* batch) {
if (this->m_texture) {
batch->QueueTexture(this);
}
}
float CSimpleTexture::GetHeight() {
float layoutHeight = CLayoutFrame::GetHeight();
if (layoutHeight != 0.0f) {
return layoutHeight;
}
uint32_t textureHeight;
if (TextureGetDimensions(this->m_texture, nullptr, &textureHeight, 0)) {
float ndcHeight = static_cast<float>(textureHeight) / (CoordinateGetAspectCompensation() * 1024.0f);
float ddcHeight = NDCToDDCWidth(ndcHeight);
return ddcHeight;
}
return 0.0f;
}
int32_t CSimpleTexture::GetScriptMetaTable() {
return CSimpleTexture::s_metatable;
}
void CSimpleTexture::GetTexCoord(C2Vector* texCoord) {
texCoord[0] = { this->m_texCoord[0].x, this->m_texCoord[0].y };
texCoord[1] = { this->m_texCoord[1].x, this->m_texCoord[1].y };
texCoord[2] = { this->m_texCoord[2].x, this->m_texCoord[2].y };
texCoord[3] = { this->m_texCoord[3].x, this->m_texCoord[3].y };
}
float CSimpleTexture::GetWidth() {
float layoutWidth = CLayoutFrame::GetWidth();
if (layoutWidth != 0.0f) {
return layoutWidth;
}
uint32_t textureWidth;
if (TextureGetDimensions(this->m_texture, &textureWidth, nullptr, 0)) {
float ndcWidth = static_cast<float>(textureWidth) / (CoordinateGetAspectCompensation() * 1024.0f);
float ddcWidth = NDCToDDCWidth(ndcWidth);
return ddcWidth;
}
return 0.0f;
}
bool CSimpleTexture::IsA(int32_t type) {
return type == CSimpleTexture::s_objectType
|| type == CScriptRegion::s_objectType
|| type == CScriptObject::s_objectType;
}
void CSimpleTexture::LoadXML(XMLNode* node, CStatus* status) {
const char* inheritsAttr = node->GetAttributeByName("inherits");
if (inheritsAttr && *inheritsAttr) {
const char* tainted;
bool locked;
XMLNode* inheritsNode = FrameXML_AcquireHashNode(inheritsAttr, tainted, locked);
if (inheritsNode) {
if (locked) {
status->Add(STATUS_WARNING, "Recursively inherited node: %s", inheritsAttr);
} else {
this->LoadXML(inheritsNode, status);
FrameXML_ReleaseHashNode(inheritsAttr);
}
} else {
status->Add(STATUS_WARNING, "Couldn't find inherited node: %s", inheritsAttr);
}
}
CScriptRegion::LoadXML(node, status);
const char* hiddenAttr = node->GetAttributeByName("hidden");
if (hiddenAttr && *hiddenAttr) {
bool hide = StringToBOOL(hiddenAttr);
if (hide) {
this->Hide();
} else {
this->Show();
}
}
int32_t wrapU = 0;
int32_t wrapV = 0;
CImVector color = { 0 };
const char* horizTileAttr = node->GetAttributeByName("horizTile");
if (horizTileAttr && *horizTileAttr) {
this->m_horizTile = StringToBOOL(horizTileAttr);
wrapU = 1;
}
const char* vertTileAttr = node->GetAttributeByName("vertTile");
if (vertTileAttr && *vertTileAttr) {
this->m_vertTile = StringToBOOL(vertTileAttr);
wrapV = 1;
}
for (XMLNode* child = node->m_child; child; child = child->m_next) {
if (!SStrCmpI(child->GetName(), "TexCoords", INT_MAX)) {
int32_t valid = 1;
float left = 0.0f;
float right = 1.0f;
float top = 0.0f;
float bottom = 1.0f;
const char* name = this->GetName();
if (!name) {
name = "<unnamed>";
}
const XMLNode* rectNode = child->GetChildByName("Rect");
if (rectNode) {
// TODO
} else {
const char* leftAttr = child->GetAttributeByName("left");
if (leftAttr && *leftAttr) {
if (this->m_horizTile) {
status->Add(
STATUS_ERROR,
"Texture %s: Invalid TexCoords value (horizTile is on)",
name
);
valid = 0;
}
left = SStrToFloat(leftAttr);
if (left < -10000.0f || left > 10000.0f) {
status->Add(
STATUS_ERROR,
"Texture %s: Invalid TexCoords value (out of range)",
name
);
valid = 0;
}
}
const char* rightAttr = child->GetAttributeByName("right");
if (rightAttr && *rightAttr) {
if (this->m_horizTile) {
status->Add(
STATUS_ERROR,
"Texture %s: Invalid TexCoords value (horizTile is on)",
name
);
valid = 0;
}
right = SStrToFloat(rightAttr);
if (right < -10000.0f || right > 10000.0f) {
status->Add(
STATUS_ERROR,
"Texture %s: Invalid TexCoords value (out of range)",
name
);
valid = 0;
}
}
const char* topAttr = child->GetAttributeByName("top");
if (topAttr && *topAttr) {
if (this->m_vertTile) {
status->Add(
STATUS_ERROR,
"Texture %s: Invalid TexCoords value (vertTile is on)",
name
);
valid = 0;
}
top = SStrToFloat(topAttr);
if (top < -10000.0f || top > 10000.0f) {
status->Add(
STATUS_ERROR,
"Texture %s: Invalid TexCoords value (out of range)",
name
);
valid = 0;
}
}
const char* bottomAttr = child->GetAttributeByName("bottom");
if (bottomAttr && *bottomAttr) {
if (this->m_vertTile) {
status->Add(
STATUS_ERROR,
"Texture %s: Invalid TexCoords value (vertTile is on)",
name
);
valid = 0;
}
bottom = SStrToFloat(bottomAttr);
if (bottom < -10000.0f || bottom > 10000.0f) {
status->Add(
STATUS_ERROR,
"Texture %s: Invalid TexCoords value (out of range)",
name
);
valid = 0;
}
}
}
if (valid) {
C2Vector coords[4];
coords[0].x = left;
coords[0].y = top;
coords[1].x = left;
coords[1].y = bottom;
coords[2].x = right;
coords[2].y = top;
coords[3].y = bottom;
coords[3].x = right;
this->SetTexCoord(coords);
if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) {
wrapU = 1;
}
if (top < 0.0f || top > 1.0f || bottom < 0.0f || bottom > 1.0f) {
wrapV = 1;
}
}
} else if (!SStrCmpI(child->GetName(), "Color", INT_MAX)) {
LoadXML_Color(child, color);
this->SetTexture(color);
} else if (!SStrCmpI(child->GetName(), "Gradient", INT_MAX)) {
// TODO
}
}
const char* v91 = node->GetAttributeByName("file");
if (v91 && *v91) {
if (this->SetTexture(v91, wrapU, wrapV, CSimpleTexture::s_textureFilterMode, ImageMode_UI)) {
if (color.value) {
this->SetVertexColor(color);
}
} else {
const char* v94 = this->GetName();
if (!v94) {
v94 = "<unnamed>";
}
status->Add(
STATUS_WARNING,
"Texture %s: Unable to load texture file %s",
v94,
v91
);
// TODO
// CTexture* texture = TextureCreateSolid(&CRAPPY_GREEN); // 0xFF00FF00
// if (this->m_texture) {
// HandleClose(this->m_texture);
// }
// this->m_texture = texture;
// this->OnRegionChanged();
}
}
const char* alphaModeAttr = node->GetAttributeByName("alphaMode");
if (alphaModeAttr && *alphaModeAttr) {
EGxBlend alphaMode;
if (StringToBlendMode(alphaModeAttr, alphaMode)) {
this->SetBlendMode(alphaMode);
}
}
const char* alphaAttr = node->GetAttributeByName("alpha");
if (alphaAttr && *alphaAttr) {
float alpha = SStrToFloat(alphaAttr);
this->SetAlpha(alpha);
}
// TODO
// - nonBlocking
}
void CSimpleTexture::OnFrameSizeChanged(const CRect& rect) {
CLayoutFrame::OnFrameSizeChanged(rect);
if (false) {
// TODO
// - something about animations?
} else {
this->SetPosition(this->m_rect, this->m_position);
}
if (this->m_horizTile || this->m_vertTile) {
this->m_updateTexCoord = 1;
this->OnRegionChanged();
}
if (this->m_parent) {
this->m_parent->NotifyScrollParent();
}
}
void CSimpleTexture::PostLoadXML(XMLNode* node, CStatus* status) {
if (this->m_parent) {
for (int32_t i = 0; i < FRAMEPOINT_NUMPOINTS; i++) {
if (this->m_points[i] && !(this->m_points[i]->m_flags & 0x8)) {
break;
}
if (i + 1 == FRAMEPOINT_NUMPOINTS) {
this->SetAllPoints(this->m_parent, 1);
break;
}
}
}
}
void CSimpleTexture::SetAlpha(float alpha) {
// Clamp
alpha = std::max(0.0f, std::min(alpha, 1.0f));
CImVector color = { 0 };
this->GetVertexColor(color);
color.a = alpha * 255.0f;
this->SetVertexColor(color);
}
void CSimpleTexture::SetBlendMode(EGxBlend blend) {
if (blend == this->m_alphaMode) {
return;
}
this->m_alphaMode = blend;
this->OnRegionChanged();
}
void CSimpleTexture::SetPosition(const CRect& rect, C3Vector* position) {
position[0] = { rect.minX, rect.maxY, this->m_layoutDepth };
position[1] = { rect.minX, rect.minY, this->m_layoutDepth };
position[2] = { rect.maxX, rect.maxY, this->m_layoutDepth };
position[3] = { rect.maxX, rect.minY, this->m_layoutDepth };
}
void CSimpleTexture::SetTexCoord(const CRect& texRect) {
this->m_texCoord[0] = { texRect.minX, texRect.minY };
this->m_texCoord[1] = { texRect.minX, texRect.maxY };
this->m_texCoord[2] = { texRect.maxX, texRect.minY };
this->m_texCoord[3] = { texRect.maxX, texRect.maxY };
}
void CSimpleTexture::SetTexCoord(const C2Vector* texCoord) {
this->m_texCoord[0] = { texCoord[0].x, texCoord[0].y };
this->m_texCoord[1] = { texCoord[1].x, texCoord[1].y };
this->m_texCoord[2] = { texCoord[2].x, texCoord[2].y };
this->m_texCoord[3] = { texCoord[3].x, texCoord[3].y };
}
int32_t CSimpleTexture::SetTexture(const char* fileName, bool wrapU, bool wrapV, EGxTexFilter filter, TextureImageMode mode) {
if (this->m_texture && fileName && TextureIsSame(this->m_texture, fileName)) {
return 1;
}
HTEXTURE texture = nullptr;
if (fileName && *fileName) {
CStatus status;
CGxTexFlags texFlags = CGxTexFlags(filter, wrapU, wrapV, 0, 0, 0, 1);
texture = TextureCreate(fileName, texFlags, &status, 23);
// TODO
// if (status.unk4 >= 2) {
// HandleClose(texture);
// SysMsgAdd(&status);
// return 0;
// }
this->m_shader = CSimpleTexture::GetImageModePixelShader(mode);
}
if (this->m_texture) {
HandleClose(this->m_texture);
}
this->m_texture = texture;
this->OnRegionChanged();
return 1;
}
int32_t CSimpleTexture::SetTexture(const CImVector& color) {
HTEXTURE texture = TextureCreateSolid(color);
if (this->m_texture) {
HandleClose(this->m_texture);
}
this->m_texture = texture;
this->OnRegionChanged();
return 1;
}