#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(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::zero() ? Math::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(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(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(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(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(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(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