Skip to content

Commit

Permalink
Cubemap DDS files now allow both the side and the mipmap-level to be …
Browse files Browse the repository at this point in the history
…set in the properties window. The entire list of images is still scrubbable. Before this change only the top-level mipmap was loaded and available for display for DDS cubemaps. Added test images for cubemaps in DDS, KTX2, and PVR formats as well as the 6 source files as PNGs.
  • Loading branch information
bluescan committed Aug 5, 2024
1 parent 6e642f8 commit bf07a9c
Show file tree
Hide file tree
Showing 16 changed files with 99 additions and 108 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ endif()
FetchContent_Declare(
tacent
GIT_REPOSITORY https://github.com/bluescan/tacent.git
GIT_TAG e823e1798b3833e34802bbd21c0c9401e0d40973
# GIT_TAG e823e1798b3833e34802bbd21c0c9401e0d40973
# GIT_TAG v0.8.17
FIND_PACKAGE_ARGS NAMES tacent
)
Expand Down
112 changes: 47 additions & 65 deletions Src/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -964,30 +964,24 @@ void Image::PopulatePicturesDDS(const tImageDDS& dds)
{
if (dds.IsCubemap())
{
int w = dds.GetWidth();
int h = dds.GetHeight();

// We want the front (+Z) to be the first image.
// Cubemaps sides use a left-hand coordinate system with +Z facing the front and +Y up. We want the front (+Z)
// to be the first image because it makes the most sense from a viewing perspective. In the tImage the sides
// are loaded in the +X,-X,+Y,-Y,+Z,-Z order. We display in +Z,-Z,+X,-X,+Y,-Y order.
const int numFaces = tFaceIndex_NumFaces;
int faceOrder[numFaces] =
{
tFaceIndex_PosZ,
tFaceIndex_NegZ,
tFaceIndex_PosX,
tFaceIndex_NegX,
tFaceIndex_PosY,
tFaceIndex_NegY
};
int faceOrder[numFaces] = { tFaceIndex_PosZ, tFaceIndex_NegZ, tFaceIndex_PosX, tFaceIndex_NegX, tFaceIndex_PosY, tFaceIndex_NegY };

tList<tLayer> layers[numFaces];
dds.GetCubemapLayers(layers);
for (int f = 0; f < int(tFaceIndex_NumFaces); f++)
{
int face = faceOrder[f];

tLayer* topMip = layers[face].First();
tAssert(topMip->PixelFormat == tPixelFormat::R8G8B8A8);
Pictures.Append(new tPicture(w, h, (tPixel4b*)topMip->Data, true));
// Grab every mipmap of each cubemap side.
for (tLayer* sideMipLayer = layers[face].First(); sideMipLayer; sideMipLayer = sideMipLayer->Next())
{
tAssert(sideMipLayer->PixelFormat == tPixelFormat::R8G8B8A8);
Pictures.Append(new tPicture(sideMipLayer->Width, sideMipLayer->Height, (tPixel4b*)sideMipLayer->Data, true));
}

// Lets reset the list so it doesn't do anything bad when destructed.
layers[face].Reset();
Expand All @@ -1014,54 +1008,42 @@ void Image::CreateAltPicturesDDS(const tImageDDS& dds)
{
if (dds.IsCubemap())
{
int width = Pictures.First()->GetWidth();
int height = Pictures.First()->GetHeight();

AltPicture.Set(width*4, height*3, tPixel4b::transparent);
int originX, originY;

// PosZ
tPicture* pic = Pictures.First();
originX = width; originY = height;
for (int y = 0; y < pic->GetHeight(); y++)
for (int x = 0; x < pic->GetWidth(); x++)
AltPicture.SetPixel(originX + x, originY + y, pic->GetPixel(x, y));

// NegZ
pic = pic->Next();
originX = 3*width; originY = height;
for (int y = 0; y < pic->GetHeight(); y++)
for (int x = 0; x < pic->GetWidth(); x++)
AltPicture.SetPixel(originX + x, originY + y, pic->GetPixel(x, y));

// PosX
pic = pic->Next();
originX = 2*width; originY = height;
for (int y = 0; y < pic->GetHeight(); y++)
for (int x = 0; x < pic->GetWidth(); x++)
AltPicture.SetPixel(originX + x, originY + y, pic->GetPixel(x, y));
int w = dds.GetWidth();
int h = dds.GetHeight();
AltPicture.Set(w*4, h*3, tPixel4b::transparent);

// NegX
pic = pic->Next();
originX = 0; originY = height;
for (int y = 0; y < pic->GetHeight(); y++)
for (int x = 0; x < pic->GetWidth(); x++)
AltPicture.SetPixel(originX + x, originY + y, pic->GetPixel(x, y));
// Cubemaps sides use a left-hand coordinate system with +Z facing the front and +Y up. We want the front (+Z)
// to be the first image because it makes the most sense from a viewing perspective. In the tImage the sides
// are loaded in the +X,-X,+Y,-Y,+Z,-Z order. We display in +Z,-Z,+X,-X,+Y,-Y order.
const int numFaces = tFaceIndex_NumFaces;
struct FaceInfo { int Face; int OriginX; int OriginY; };
FaceInfo faceInfo[numFaces] =
{
{ tFaceIndex_PosZ, w, h },
{ tFaceIndex_NegZ, 3*w, h },
{ tFaceIndex_PosX, 2*w, h },
{ tFaceIndex_NegX, 0, h },
{ tFaceIndex_PosY, w, 2*h },
{ tFaceIndex_NegY, w, 0 }
};

// PosY
pic = pic->Next();
originX = width; originY = 2*height;
for (int y = 0; y < pic->GetHeight(); y++)
for (int x = 0; x < pic->GetWidth(); x++)
AltPicture.SetPixel(originX + x, originY + y, pic->GetPixel(x, y));
tList<tLayer> layers[numFaces];
dds.GetCubemapLayers(layers);
for (int f = 0; f < int(tFaceIndex_NumFaces); f++)
{
int face = faceInfo[f].Face;
int originX = faceInfo[f].OriginX;
int originY = faceInfo[f].OriginY;

// NegY
pic = pic->Next();
originX = width; originY = 0;
for (int y = 0; y < pic->GetHeight(); y++)
for (int x = 0; x < pic->GetWidth(); x++)
AltPicture.SetPixel(originX + x, originY + y, pic->GetPixel(x, y));
tLayer* topMip = layers[face].First();
tAssert(topMip->PixelFormat == tPixelFormat::R8G8B8A8);
for (int y = 0; y < topMip->Height; y++)
for (int x = 0; x < topMip->Width; x++)
AltPicture.SetPixel(originX + x, originY + y, topMip->GetPixel(x, y));

// Let's reset the list so it doesn't do anything bad when destructed.
layers[face].Reset();
}
AltPictureTyp = AltPictureType::CubemapTLayout;
}
else if (dds.IsMipmapped())
Expand All @@ -1074,17 +1056,17 @@ void Image::CreateAltPicturesDDS(const tImageDDS& dds)
AltPicture.Set(width, height, tPixel4b::transparent);
int originY = 0;
int originX = 0;
for (tPicture* layer = Pictures.First(); layer; layer = layer->Next())
for (tPicture* mipPic = Pictures.First(); mipPic; mipPic = mipPic->Next())
{
for (int y = 0; y < layer->GetHeight(); y++)
for (int y = 0; y < mipPic->GetHeight(); y++)
{
for (int x = 0; x < layer->GetWidth(); x++)
for (int x = 0; x < mipPic->GetWidth(); x++)
{
tPixel4b pixel = layer->GetPixel(x, y);
tPixel4b pixel = mipPic->GetPixel(x, y);
AltPicture.SetPixel(originX + x, y, pixel);
}
}
originX += layer->GetWidth();
originX += mipPic->GetWidth();
}
AltPictureTyp = AltPictureType::MipmapSideBySide;
}
Expand Down
90 changes: 48 additions & 42 deletions Src/Properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,16 @@ using namespace tImage;

namespace Viewer
{
// These return true if any UI was drawn.
bool DoAltMipmapsDisplay(tString& texTypeName);
bool DoAltCubemapDisplay(tString& texTypeName);
bool DoChooseDisplayImage(tString& texTypeName, float itemWidth);
// Returns true if any UI was drawn. Currently called for DDS, KTX, KTX2, and PVR files.
bool DoMultiSurface(float itemWidth);
}


bool Viewer::DoAltMipmapsDisplay(tString& texTypeName)
bool Viewer::DoMultiSurface(float itemWidth)
{
bool anyDraw = false;
bool altMipmapsPicAvail = CurrImage->IsAltMipmapsPictureAvail() && !CropMode;
bool altMipmapsPicEnabl = altMipmapsPicAvail && CurrImage->IsAltPictureEnabled();
bool anyDraw = false;

if (altMipmapsPicAvail)
{
if (ImGui::Checkbox("Display All Mipmaps", &altMipmapsPicEnabl))
Expand All @@ -46,20 +43,11 @@ bool Viewer::DoAltMipmapsDisplay(tString& texTypeName)
CurrImage->Bind();
}
Gutil::ToolTip("Display all mipmaps in a single image.");
texTypeName = "Mipmap";
anyDraw = true;
}

return anyDraw;
}


bool Viewer::DoAltCubemapDisplay(tString& texTypeName)
{
bool altCubemapPicAvail = CurrImage->IsAltCubemapPictureAvail() && !CropMode;
bool altCubemapPicEnabl = altCubemapPicAvail && CurrImage->IsAltPictureEnabled();
bool anyDraw = false;

if (altCubemapPicAvail)
{
if (ImGui::Checkbox("Display As Cubemap", &altCubemapPicEnabl))
Expand All @@ -68,36 +56,63 @@ bool Viewer::DoAltCubemapDisplay(tString& texTypeName)
CurrImage->Bind();
}
Gutil::ToolTip("Display all cubemap sides in a T-layout.");
texTypeName = "Cube Side";
anyDraw = true;
}

return anyDraw;
}


bool Viewer::DoChooseDisplayImage(tString& texTypeName, float itemWidth)
{
bool anyDraw = false;
int numTextures = CurrImage->GetNumFrames();
if (numTextures >= 2)
if ((numTextures >= 2) && (!CurrImage->IsAltPictureEnabled()))
{
if (!CurrImage->IsAltPictureEnabled())
if (altCubemapPicAvail)
{
tString imageNumText;
tsPrintf(imageNumText, "%s (%d)", texTypeName.Chr(), numTextures);
int numCubeSurfs = tFaceIndex_NumFaces;
int numCubeMips = numTextures / numCubeSurfs;
int oneBasedSurfNum = CurrImage->FrameNum/numCubeMips + 1;
int oneBasedMipNum = CurrImage->FrameNum - ((oneBasedSurfNum-1)*numCubeMips) + 1;

tString surfNumText;
tsPrintf(surfNumText, "Cube Side (%d)", numCubeSurfs);
ImGui::SetNextItemWidth(itemWidth);
if (ImGui::InputInt(surfNumText.Chr(), &oneBasedSurfNum))
{
tMath::tiClamp(oneBasedSurfNum, 1, numCubeSurfs);
CurrImage->FrameNum = (oneBasedSurfNum-1)*numCubeMips + (oneBasedMipNum-1);
}
ImGui::SameLine(); Gutil::HelpMark
(
"Cubemap side to display. Cubemaps use a left-handed ordering\n"
"with +Z facing forward and +Y up. Sides are shown in the order\n"
"+Z,-Z,+X,-X,+Y,-Y. That is, front, back, right, left, top, bottom."
);

if (numCubeMips > 1)
{
tString mipNumText;
tsPrintf(mipNumText, "Cube Mip (%d)", numCubeMips);
ImGui::SetNextItemWidth(itemWidth);
if (ImGui::InputInt(mipNumText.Chr(), &oneBasedMipNum))
{
tMath::tiClamp(oneBasedMipNum, 1, numCubeMips);
CurrImage->FrameNum = (oneBasedSurfNum-1)*numCubeMips + (oneBasedMipNum-1);
}
ImGui::SameLine(); Gutil::HelpMark("Which cubemap mipmap to display.");
}
}
else
{
tString imageNumText;
tsPrintf(imageNumText, "%s (%d)", altMipmapsPicAvail ? "Mipmap" : "Texture", numTextures);
int oneBasedTextureNum = CurrImage->FrameNum + 1;
ImGui::SetNextItemWidth(itemWidth);
if (ImGui::InputInt(imageNumText.Chr(), &oneBasedTextureNum))
{
CurrImage->FrameNum = oneBasedTextureNum - 1;
tMath::tiClamp(CurrImage->FrameNum, 0, numTextures-1);
}
ImGui::SameLine(); Gutil::HelpMark("Which mipmap or cubemap side to display.\nCubemap sides left-handed +X,-X,+Y,-Y,+Z,-Z");

ImGui::SameLine(); Gutil::HelpMark("Which mipmap or texture to display.");
}
anyDraw = true;
}

return anyDraw;
}

Expand Down Expand Up @@ -147,10 +162,7 @@ void Viewer::ShowPropertiesWindow(bool* popen)
int numTextures = CurrImage->GetNumFrames();
bool reloadChanges = false;

tString texTypeName = "Texture";
anyUIDisplayed |= DoAltMipmapsDisplay(texTypeName);
anyUIDisplayed |= DoAltCubemapDisplay(texTypeName);
anyUIDisplayed |= DoChooseDisplayImage(texTypeName, itemWidth);
anyUIDisplayed |= DoMultiSurface(itemWidth);

if (tIsETCFormat(CurrImage->Info.SrcPixelFormat))
{
Expand Down Expand Up @@ -278,10 +290,7 @@ void Viewer::ShowPropertiesWindow(bool* popen)
int numTextures = CurrImage->GetNumFrames();
bool reloadChanges = false;

tString texTypeName = "Texture";
anyUIDisplayed |= DoAltMipmapsDisplay(texTypeName);
anyUIDisplayed |= DoAltCubemapDisplay(texTypeName);
anyUIDisplayed |= DoChooseDisplayImage(texTypeName, itemWidth);
anyUIDisplayed |= DoMultiSurface(itemWidth);

// If we're here show options when have 1 or more frames.
bool altEnabled = CurrImage->IsAltPictureEnabled();
Expand Down Expand Up @@ -413,10 +422,7 @@ void Viewer::ShowPropertiesWindow(bool* popen)
int numTextures = CurrImage->GetNumFrames();
bool reloadChanges = false;

tString texTypeName = "Texture";
anyUIDisplayed |= DoAltMipmapsDisplay(texTypeName);
anyUIDisplayed |= DoAltCubemapDisplay(texTypeName);
anyUIDisplayed |= DoChooseDisplayImage(texTypeName, itemWidth);
anyUIDisplayed |= DoMultiSurface(itemWidth);

if (tIsETCFormat(CurrImage->Info.SrcPixelFormat))
{
Expand Down
Binary file added TestImages/Cubemaps/CubemapSide1PX_Right.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added TestImages/Cubemaps/CubemapSide2NX_Left.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added TestImages/Cubemaps/CubemapSide3PY_Top.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added TestImages/Cubemaps/CubemapSide4NY_Bottom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added TestImages/Cubemaps/CubemapSide5PZ_Front.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added TestImages/Cubemaps/CubemapSide6NZ_Back.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added TestImages/Cubemaps/Cubemap_DDS_BC1.dds
Binary file not shown.
Binary file added TestImages/Cubemaps/Cubemap_DDS_BC1_M.dds
Binary file not shown.
Binary file added TestImages/Cubemaps/Cubemap_KTX2_ASTC8x8.ktx2
Binary file not shown.
Binary file added TestImages/Cubemaps/Cubemap_KTX2_ASTC8x8_M.ktx2
Binary file not shown.
Binary file added TestImages/Cubemaps/Cubemap_PVR_ASTC8x8.pvr
Binary file not shown.
Binary file added TestImages/Cubemaps/Cubemap_PVR_ASTC8x8_M.pvr
Binary file not shown.
3 changes: 3 additions & 0 deletions TestImages/Cubemaps/Readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The png images are the lossless individual images of the test cubemap. They are specififed in a left-handed coordinate system with +Z facing front and +Y up.

The cubemap images in the DDS, KTX2, and PVR files were all generated using PVRTexTool. An "_M" in the name means the cubemaps have mipmaps.

0 comments on commit bf07a9c

Please sign in to comment.