-
Notifications
You must be signed in to change notification settings - Fork 1
Doesn't currently deswizzle uncompressed PS3 textures #4
Copy link
Copy link
Open
Description
PS3 textures which are uncompressed use Morton swizzling for quick reading from the GPU. This makes them look like garbage when read expecting a normal texture. Since it's a lossless transformation it's possible to undo it to get the proper expected texture data.
See the logic in vtfpp under MIT license here. This routine is used in 4 tools I've made (VPKEdit, MareTF, VTF Thumbnailer, QVTF++) to compile/decompile PS3 VTFs and I haven't had issue with it yet.
[[nodiscard]] constexpr uint32_t log2ceil(uint32_t value) {
// https://github.com/lastland/Tricks-Museum/blob/master/FastLog2/FastLog2.c
return ((std::bit_cast<uint32_t>(static_cast<float>(value)) >> 23) & 0xff) - 127;
}
template<bool ExistingDataIsSwizzled>
constexpr void swizzleImageDataForPS3(std::span<std::byte> inputData, std::span<std::byte> outputData, ImageFormat format, uint16_t width, uint16_t height, uint16_t slice) {
width *= ImageFormatDetails::bpp(format) / 32;
const auto zIndex = [
widthL2 = static_cast<int>(log2ceil(width)),
heightL2 = static_cast<int>(log2ceil(height)),
sliceL2 = static_cast<int>(log2ceil(slice))
](uint32_t x, uint32_t y, uint32_t z) {
auto widthL2m = widthL2;
auto heightL2m = heightL2;
auto sliceL2m = sliceL2;
uint32_t offset = 0;
uint32_t shiftCount = 0;
do {
if (sliceL2m --> 0) {
offset |= (z & 1) << shiftCount++;
z >>= 1;
}
if (heightL2m --> 0) {
offset |= (y & 1) << shiftCount++;
y >>= 1;
}
if (widthL2m --> 0) {
offset |= (x & 1) << shiftCount++;
x >>= 1;
}
} while (x | y | z);
return offset;
};
const auto* inputPtr = reinterpret_cast<const uint32_t*>(inputData.data());
auto* outputPtr = reinterpret_cast<uint32_t*>(outputData.data());
for (uint16_t x = 0; x < width; x++) {
for (uint16_t y = 0; y < height; y++) {
for (uint16_t z = 0; z < slice; z++) {
if constexpr (ExistingDataIsSwizzled) {
*outputPtr++ = reinterpret_cast<const uint32_t*>(inputData.data())[zIndex(x, y, z)];
} else {
reinterpret_cast<uint32_t*>(outputData.data())[zIndex(x, y, z)] = *inputPtr++;
}
}
}
}
}
template<bool ConvertingFromSource>
void swapImageDataEndianForConsole(std::span<std::byte> imageData, ImageFormat format, uint8_t mipCount, uint16_t frameCount, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t sliceCount, VTF::Platform platform) {
// ... x360 dxt endian stuff here ... //
if ((platform == VTF::PLATFORM_PS3_ORANGEBOX || platform == VTF::PLATFORM_PS3_PORTAL2) && !ImageFormatDetails::compressed(format) && ImageFormatDetails::bpp(format) % 32 == 0) {
std::vector<std::byte> out(imageData.size());
for(int mip = mipCount - 1; mip >= 0; mip--) {
const auto mipWidth = ImageDimensions::getMipDim(mip, width);
const auto mipHeight = ImageDimensions::getMipDim(mip, height);
for (int frame = 0; frame < frameCount; frame++) {
for (int face = 0; face < faceCount; face++) {
if (uint32_t offset, length; ImageFormatDetails::getDataPosition(offset, length, format, mip, mipCount, frame, frameCount, face, faceCount, width, height)) {
std::span imageDataSpan{imageData.data() + offset, length * sliceCount};
std::span outSpan{out.data() + offset, length * sliceCount};
::swizzleImageDataForPS3<ConvertingFromSource>(imageDataSpan, outSpan, format, mipWidth, mipHeight, sliceCount);
}
}
}
}
std::memcpy(imageData.data(), out.data(), out.size());
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels