Files
ss/src/egg/gfx/eggCpuTexture.cpp
T
2026-05-07 19:53:30 +02:00

360 lines
9.4 KiB
C++

#include "egg/gfx/eggCpuTexture.h"
#include "common.h"
#include "egg/core/eggHeap.h"
#include "egg/gfx/eggGXUtility.h"
#include "egg/gfx/eggTexture.h"
#include "egg/math/eggVector.h"
#include "rvl/GX/GXTexture.h"
#include "rvl/GX/GXTypes.h"
#include "rvl/MTX/vec.h"
#include "rvl/OS/OSCache.h"
namespace EGG {
namespace {
void blendColor(GXColor &outColor, const GXColor &c1, const GXColor &c2, f32 amount) {
if (amount <= 0.0f) {
outColor = c1;
} else if (amount >= 1.0f) {
outColor = c2;
} else {
outColor.r = c1.r + amount * (c2.r - c1.r);
outColor.g = c1.g + amount * (c2.g - c1.g);
outColor.b = c1.b + amount * (c2.b - c1.b);
outColor.a = c1.a + amount * (c2.a - c1.a);
}
}
void makeGradient(int op, GXColor *outColor, u16 size, u16 start, u16 end, const GXColor &c1, const GXColor &c2) {
for (int i = 0; i < start + 1; i++) {
outColor[i] = c1;
}
for (int i = end - 1; i < size; i++) {
outColor[i] = c2;
}
for (int i = start + 1; i < end - 1; i++) {
f32 ratio = (f32)(i - start) / (f32)(end - start);
switch (op) {
case 0: break;
case 1: ratio *= ratio; break;
case 2:
ratio *= ratio;
ratio *= ratio;
break;
case 3:
ratio *= ratio;
ratio *= ratio;
ratio *= ratio;
break;
case 4:
ratio *= ratio;
ratio *= ratio;
ratio *= ratio;
ratio *= ratio;
break;
case 5: ratio = -(ratio - 1.0f) * (ratio - 1.0f) + 1.0f; break;
case 6:
ratio -= 1.0f;
ratio = -ratio * ratio * ratio * ratio + 1.0f;
break;
}
blendColor(outColor[i], c1, c2, ratio);
}
}
} // namespace
CpuTexture::CpuTexture() {
mWidth = 0;
mHeight = 0;
mFlags = 0;
mTexFormat = GX_TF_RGBA8;
mWrapS = 0;
mWrapT = 0;
mMinFilt = 1;
mMagFilt = 1;
dataPtr = nullptr;
mpBuffer = nullptr;
}
CpuTexture::CpuTexture(u16 width, u16 height, GXTexFmt texFmt) {
mWidth = width;
mHeight = height;
mFlags = 0;
mTexFormat = texFmt;
mWrapS = 0;
mWrapT = 0;
mMinFilt = 1;
mMagFilt = 1;
dataPtr = nullptr;
mpBuffer = nullptr;
}
CpuTexture::CpuTexture(const GXTexObj *pObj) {
mWidth = GXGetTexObjWidth(pObj);
mHeight = GXGetTexObjHeight(pObj);
mTexFormat = GXGetTexObjFmt(pObj);
mWrapS = GXGetTexObjWrapS(pObj);
mWrapT = GXGetTexObjWrapT(pObj);
mMinFilt = GXGetTexObjMinFilt(pObj);
mMagFilt = GXGetTexObjMagFilt(pObj);
// Convert Physical to Virtual Address
dataPtr = (char *)GXGetTexObjData(pObj) - 0x80000000;
mpBuffer = nullptr;
}
CpuTexture::~CpuTexture() {
if ((mFlags & NEEDS_BUFFER_FREE) != 0) {
delete[] mpBuffer;
}
}
void CpuTexture::configure() {
mFlags = CONFIGURED;
}
void CpuTexture::setBuffer(void *buf) {
if (buf != nullptr) {
dataPtr = buf;
}
mFlags &= ~HAS_HEADER;
}
void CpuTexture::load(GXTexMapID id) const {
GXTexObj obj;
getTexObj(&obj);
GXLoadTexObj(&obj, id);
}
void CpuTexture::getTexObj(GXTexObj *pObj) const {
GXInitTexObj(pObj, dataPtr, mWidth, mHeight, getFormat(), (GXTexWrapMode)mWrapS, (GXTexWrapMode)mWrapT, false);
GXInitTexObjLOD(pObj, (GXTexFilter)mMinFilt, (GXTexFilter)mMagFilt, 0.0f, 0.0f, 0.0f, false, false, GX_ANISO_1);
}
void CpuTexture::flush() const {
size_t size = GXGetTexBufferSize(mWidth, mHeight, getFormat(), false, true);
DCFlushRange(dataPtr, size);
}
void CpuTexture::buildHeader() const {
ResTIMG *pHdr = getHeader();
pHdr->format = mTexFormat;
pHdr->alphaEnabled = 1;
pHdr->width = mWidth;
pHdr->height = mHeight;
pHdr->wrapS = mWrapS;
pHdr->wrapT = mWrapT;
pHdr->indexTexture = 0;
pHdr->colorFormat = 0;
pHdr->numColors = 0;
pHdr->paletteOffset = 0;
pHdr->mipmapEnabled = 0;
pHdr->doEdgeLOD = 0;
pHdr->biasClamp = 0;
pHdr->maxAnisotropy = 0;
pHdr->minFilter = mMinFilt;
pHdr->magFilter = mMagFilt;
pHdr->minLOD = 0;
pHdr->maxLOD = 0;
pHdr->mipmapCount = 1;
pHdr->LODBias = 0;
pHdr->imageOffset = sizeof(ResTIMG);
}
extern "C" double sqrt(double);
void CpuTexture::fillNormalMapSphere(f32 f1, f32 f2) {
f32 mid = static_cast<f32>(mWidth / 2);
for (u16 y = 0; y < mHeight; y++) {
for (u16 x = 0; x < mWidth; x++) {
Vector3f vec;
GXColor c;
f32 fx = f1 * ((x + f2) - mid);
f32 fy = f1 * -((y + f2) - mid);
f32 mid2 = mid * mid;
f32 fz = mid2 - (fx * fx + fy * fy);
vec(0) = fx;
vec(1) = fy;
// TODO: std::sqrtf?
vec(2) = fz < Math<f32>::zero() ? Math<f32>::zero() : (f32)sqrt(fz);
VECNormalize(vec, vec);
GXUtility::getNormalColor(c, vec);
c.r = c.a;
setColor(x, y, c);
}
}
flush();
}
void CpuTexture::fillGradient(
int op, int unk, u16 start, u16 end, const GXColor &c1, const GXColor &c2, bool b1, bool b2
) {
GXColor gradient[1024];
GXColor colors[256];
bool swapMode = unk == 0x73 || unk == 0x53;
u16 width = swapMode ? mWidth : mHeight;
u16 height = swapMode ? mHeight : mWidth;
u16 mid = width / 2;
makeGradient(op, gradient, width, start, end, c1, c2);
if (b1) {
for (int i = 0; i < width; i++) {
colors[i] = gradient[i < mid ? i + mid : i - mid];
}
for (int i = 0; i < width; i++) {
gradient[i] = colors[i];
}
}
u16 start2 = b2 ? 0 : start;
u32 end2 = b2 ? width : end;
for (u16 x = start2; x < end2; x++) {
for (u16 y = 0; y < height; y++) {
if (swapMode) {
setColor(x, y, gradient[x]);
} else {
setColor(y, x, gradient[x]);
}
}
}
flush();
}
void CpuTexture::alloc(Heap *pHeap) {
Heap *heap = !pHeap ? Heap::getCurrentHeap() : pHeap;
size_t size = GXGetTexBufferSize(mWidth, mHeight, getFormat(), false, true);
mpBuffer = new (heap, 0x20) char[size];
setBuffer(mpBuffer);
mFlags |= NEEDS_BUFFER_FREE;
}
void CpuTexture::allocWithHeaderDebug(Heap *pHeap) {
Heap *heap = !pHeap ? Heap::getCurrentHeap() : pHeap;
size_t size = GXGetTexBufferSize(mWidth, mHeight, getFormat(), false, true);
// Textures need 32-bit alignment ( ptr is represented with ptr >> 5 )
mpBuffer = new (heap, 0x20) char[size + sizeof(ResTIMG)];
// Texture has Header -> offset to actual image data
void *imageData = mpBuffer + sizeof(ResTIMG);
setBuffer(imageData);
if (imageData != nullptr) {
mFlags |= HAS_HEADER;
}
mFlags |= NEEDS_BUFFER_FREE;
buildHeader();
}
void CpuTexture::allocate(Heap *pHeap) {
alloc(pHeap);
}
void CpuTexture::setColor(u16 x, u16 y, GXColor color) {
// NONMATCHING
switch (getFormat()) {
case GX_TF_RGBA8:
case GX_TF_Z24X8: {
int pix = (x & 0x3) * 2 + (y & 0x3) * 8;
int block = (x >> 2) + (y >> 2) * (getWidth() / 4);
int offset = block * 0x40 + pix;
u8 *dat = static_cast<u8 *>(getBuffer()) + offset;
dat[0] = color.a;
dat[1] = color.r;
dat[0x20] = color.g;
dat[0x21] = color.b;
break;
}
case GX_TF_I8:
case GX_TF_Z8: {
int offset = (x & 0x7) + (y & 0x3) * 8; // pixel
offset += ((x >> 3) + ((y >> 2) * (getWidth() / 8))) * 32; // block
u8 *dat = static_cast<u8 *>(getBuffer()) + offset;
dat[0] = color.r;
break;
}
case GX_TF_IA8:
case GX_TF_Z16: {
int block = (x >> 2) + (y >> 2) * (getWidth() / 4);
int pix = (x & 0x3) * 2 + (y & 0x3) * 8;
int offset = block * 0x20 + pix;
u8 *dat = static_cast<u8 *>(getBuffer()) + offset;
dat[0] = color.a;
dat[1] = color.r;
break;
}
default: break;
}
}
GXColor CpuTexture::getColor(u16 x, u16 y) const {
GXColor c;
switch (getFormat()) {
case GX_TF_RGBA8:
case GX_TF_Z24X8: {
int offset = ((x >> 2) + (y >> 2) * (getWidth() / 4)) * 0x40 + (y & 3) * 8 + (x & 3) * 2;
u8 *dat = static_cast<u8 *>(getBuffer()) + offset;
c.r = dat[1];
c.g = dat[0x20];
c.b = dat[0x21];
c.a = dat[0];
break;
}
case GX_TF_I8:
case GX_TF_Z8: {
// TODO - probably fake
int idx = (x & 0x7);
int pixel_idx = (y & 0x3) * 8;
int block_idx = ((getWidth() / 8) * (y >> 2) + (x >> 3));
idx = idx + pixel_idx + block_idx * 0x20;
u8 *dat = static_cast<u8 *>(getBuffer()) + idx;
c.a = dat[0];
c.b = dat[0];
c.g = dat[0];
c.r = dat[0];
break;
}
case GX_TF_IA8:
case GX_TF_Z16: {
int offset = ((x >> 2) + (y >> 2) * (getWidth() / 4)) * 0x20 + (y & 3) * 8 + (x & 3) * 2;
u8 *dat = static_cast<u8 *>(getBuffer()) + offset;
c.a = dat[0];
c.b = dat[1];
c.g = c.b;
c.r = c.b;
break;
}
default: break;
}
return c;
}
} // namespace EGG