Rebase TextureImporting onto cmake branch
This commit is contained in:
parent
b507196851
commit
d969c0d053
|
@ -1,3 +1,6 @@
|
|||
[submodule "externals/LibCommon"]
|
||||
path = externals/LibCommon
|
||||
url = ../LibCommon.git
|
||||
[submodule "externals/stb"]
|
||||
path = externals/stb
|
||||
url = https://github.com/nothings/stb
|
||||
|
|
|
@ -39,5 +39,7 @@ set(CODEGEN_GENERATE_INSTALL_TARGETS OFF)
|
|||
set(CODEGEN_BUILD_PACKAGE_DURING_CONFIGURE ON)
|
||||
add_subdirectory(externals/LibCommon)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/externals/stb)
|
||||
|
||||
add_subdirectory(src/Core)
|
||||
add_subdirectory(src/Editor)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1034f5e5c4809ea0a7f4387e0cd37c5184de3cdd
|
|
@ -4,7 +4,7 @@
|
|||
#include "CResourceStore.h"
|
||||
#include "CVirtualDirectory.h"
|
||||
#include "Core/Resource/CResTypeInfo.h"
|
||||
#include "Core/Resource/EResType.h"
|
||||
#include "Core/Resource/EResourceType.h"
|
||||
#include <Common/CAssetID.h>
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/Flags.h>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define CRESOURCESTORE_H
|
||||
|
||||
#include "CVirtualDirectory.h"
|
||||
#include "Core/Resource/EResType.h"
|
||||
#include "Core/Resource/EResourceType.h"
|
||||
#include <Common/CAssetID.h>
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/FileUtil.h>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define CVIRTUALDIRECTORY
|
||||
|
||||
/* Virtual directory system used to look up resources by their location in the filesystem. */
|
||||
#include "Core/Resource/EResType.h"
|
||||
#include "Core/Resource/EResourceType.h"
|
||||
#include <Common/Macros.h>
|
||||
#include <Common/TString.h>
|
||||
#include <vector>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef NCORETESTS_H
|
||||
#define NCORETESTS_H
|
||||
|
||||
#include "Core/Resource/EResType.h"
|
||||
#include "Core/Resource/EResourceType.h"
|
||||
|
||||
/** Unit tests for Core */
|
||||
namespace NCoreTests
|
||||
|
|
|
@ -46,9 +46,10 @@ void CFramebuffer::Init()
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
|
||||
|
||||
mpRenderbuffer = new CRenderbuffer(mWidth, mHeight);
|
||||
mpTexture = new CTexture(mWidth, mHeight);
|
||||
mpRenderbuffer->SetMultisamplingEnabled(mEnableMultisampling);
|
||||
mpTexture = new CTexture(mWidth, mHeight);
|
||||
mpTexture->SetMultisamplingEnabled(mEnableMultisampling);
|
||||
mpTexture->CreateRenderResources();
|
||||
InitBuffers();
|
||||
mInitialized = true;
|
||||
}
|
||||
|
@ -101,9 +102,9 @@ void CFramebuffer::InitBuffers()
|
|||
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mpRenderbuffer->BufferID()
|
||||
);
|
||||
|
||||
mpTexture->Bind(0);
|
||||
mpTexture->BindToSampler(0);
|
||||
glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, (mEnableMultisampling ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D), mpTexture->TextureID(), 0
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, (mEnableMultisampling ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D), mpTexture->RenderResource(), 0
|
||||
);
|
||||
|
||||
mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define CFRAMEBUFFER_H
|
||||
|
||||
#include "CRenderbuffer.h"
|
||||
#include "Core/Resource/CTexture.h"
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
#include <GL/glew.h>
|
||||
|
||||
class CFramebuffer
|
||||
|
|
|
@ -237,7 +237,7 @@ void CDrawUtil::DrawBillboard(CTexture* pTexture, const CVector3f& Position, con
|
|||
static GLuint TintLoc = mpBillboardShader->GetUniformLocation("TintColor");
|
||||
glUniform4f(TintLoc, Tint.R, Tint.G, Tint.B, Tint.A);
|
||||
|
||||
pTexture->Bind(0);
|
||||
pTexture->BindToSampler(0);
|
||||
|
||||
// Set other properties
|
||||
CMaterial::KillCachedMaterial();
|
||||
|
@ -271,8 +271,8 @@ void CDrawUtil::DrawLightBillboard(ELightType Type, const CColor& LightColor, co
|
|||
|
||||
CTexture *pTexA = GetLightTexture(Type);
|
||||
CTexture *pTexB = GetLightMask(Type);
|
||||
pTexA->Bind(0);
|
||||
pTexB->Bind(1);
|
||||
pTexA->BindToSampler(0);
|
||||
pTexB->BindToSampler(1);
|
||||
|
||||
static GLuint TextureLoc = mpLightBillboardShader->GetUniformLocation("Texture");
|
||||
static GLuint MaskLoc = mpLightBillboardShader->GetUniformLocation("LightMask");
|
||||
|
@ -360,7 +360,7 @@ CShader* CDrawUtil::GetTextShader()
|
|||
void CDrawUtil::LoadCheckerboardTexture(uint32 GLTextureUnit)
|
||||
{
|
||||
Init();
|
||||
mpCheckerTexture->Bind(GLTextureUnit);
|
||||
mpCheckerTexture->BindToSampler(GLTextureUnit);
|
||||
}
|
||||
|
||||
CTexture* CDrawUtil::GetLightTexture(ELightType Type)
|
||||
|
|
|
@ -188,7 +188,7 @@ void CRenderer::RenderBloom()
|
|||
|
||||
CDrawUtil::UseTextureShader();
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
|
||||
mPostProcessFramebuffer.Texture()->Bind(0);
|
||||
mPostProcessFramebuffer.Texture()->BindToSampler(0);
|
||||
CDrawUtil::DrawSquare();
|
||||
|
||||
// Pass 2: Horizontal blur
|
||||
|
@ -197,7 +197,7 @@ void CRenderer::RenderBloom()
|
|||
|
||||
CDrawUtil::UseTextureShader(CColor::skGray);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
mBloomFramebuffers[0].Texture()->Bind(0);
|
||||
mBloomFramebuffers[0].Texture()->BindToSampler(0);
|
||||
CDrawUtil::DrawSquare();
|
||||
|
||||
for (uint32 iPass = 0; iPass < 6; iPass++)
|
||||
|
@ -217,7 +217,7 @@ void CRenderer::RenderBloom()
|
|||
|
||||
CDrawUtil::UseTextureShader(CColor::skGray);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
mBloomFramebuffers[1].Texture()->Bind(0);
|
||||
mBloomFramebuffers[1].Texture()->BindToSampler(0);
|
||||
CDrawUtil::DrawSquare();
|
||||
|
||||
for (uint32 iPass = 0; iPass < 6; iPass++)
|
||||
|
@ -240,7 +240,7 @@ void CRenderer::RenderBloom()
|
|||
|
||||
CDrawUtil::UseTextureShader();
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
mBloomFramebuffers[2].Texture()->Bind(0);
|
||||
mBloomFramebuffers[2].Texture()->BindToSampler(0);
|
||||
CDrawUtil::DrawSquare();
|
||||
|
||||
if (mBloomMode == EBloomMode::BloomMaps)
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "Core/OpenGL/CFramebuffer.h"
|
||||
#include "Core/Resource/CFont.h"
|
||||
#include "Core/Resource/CLight.h"
|
||||
#include "Core/Resource/CTexture.h"
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
#include "Core/Scene/CSceneNode.h"
|
||||
|
||||
#include <Common/CColor.h>
|
||||
|
|
|
@ -42,7 +42,7 @@ CVector2f CFont::RenderString(const TString& rkString, CRenderer* /*pRenderer*/,
|
|||
GLuint ModelMtxLoc = pTextShader->GetUniformLocation("ModelMtx");
|
||||
GLuint ColorLoc = pTextShader->GetUniformLocation("FontColor");
|
||||
GLuint LayerLoc = pTextShader->GetUniformLocation("RGBALayer");
|
||||
mpFontTexture->Bind(0);
|
||||
mpFontTexture->BindToSampler(0);
|
||||
smGlyphVertices->Bind();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define CFONT_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CTexture.h"
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
#include "TResPtr.h"
|
||||
#include "Core/Resource/Model/CVertex.h"
|
||||
#include "Core/OpenGL/CDynamicVertexBuffer.h"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define MATERIAL_H
|
||||
|
||||
#include "CMaterialPass.h"
|
||||
#include "CTexture.h"
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
#include "TResPtr.h"
|
||||
#include "Core/Resource/Model/EVertexAttribute.h"
|
||||
#include "Core/Render/FRenderOptions.h"
|
||||
|
|
|
@ -81,7 +81,7 @@ void CMaterialPass::HashParameters(CFNV1A& rHash)
|
|||
void CMaterialPass::LoadTexture(uint32 PassIndex)
|
||||
{
|
||||
if (mpTexture)
|
||||
mpTexture->Bind(PassIndex);
|
||||
mpTexture->BindToSampler(PassIndex);
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAnimCurrent(FRenderOptions Options, uint32 PassIndex)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define CMATERIALPASS_H
|
||||
|
||||
#include "TResPtr.h"
|
||||
#include "CTexture.h"
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
#include "ETevEnums.h"
|
||||
#include "Core/Render/FRenderOptions.h"
|
||||
#include <Common/CFourCC.h>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define CMATERIALSET_H
|
||||
|
||||
#include "CMaterial.h"
|
||||
#include "CTexture.h"
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
#include <Common/EGame.h>
|
||||
#include <Common/FileIO/IInputStream.h>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef CRESTYPEFILTER_H
|
||||
#define CRESTYPEFILTER_H
|
||||
|
||||
#include "EResType.h"
|
||||
#include "EResourceType.h"
|
||||
#include "CResTypeInfo.h"
|
||||
#include "Core/GameProject/CResourceEntry.h"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef CRESTYPEINFO
|
||||
#define CRESTYPEINFO
|
||||
|
||||
#include "EResType.h"
|
||||
#include "EResourceType.h"
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/EGame.h>
|
||||
#include <Common/Flags.h>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define CRESOURCE_H
|
||||
|
||||
#include "CResTypeInfo.h"
|
||||
#include "EResType.h"
|
||||
#include "EResourceType.h"
|
||||
#include "Core/GameProject/CDependencyTree.h"
|
||||
#include "Core/GameProject/CResourceEntry.h"
|
||||
#include "Core/GameProject/CResourceStore.h"
|
||||
|
|
|
@ -1,372 +0,0 @@
|
|||
#include "CTexture.h"
|
||||
#include <cmath>
|
||||
|
||||
CTexture::CTexture(CResourceEntry *pEntry /*= 0*/)
|
||||
: CResource(pEntry)
|
||||
, mTexelFormat(ETexelFormat::RGBA8)
|
||||
, mSourceTexelFormat(ETexelFormat::RGBA8)
|
||||
, mWidth(0)
|
||||
, mHeight(0)
|
||||
, mNumMipMaps(0)
|
||||
, mLinearSize(0)
|
||||
, mEnableMultisampling(false)
|
||||
, mBufferExists(false)
|
||||
, mpImgDataBuffer(nullptr)
|
||||
, mImgDataSize(0)
|
||||
, mGLBufferExists(false)
|
||||
{
|
||||
}
|
||||
|
||||
CTexture::CTexture(uint32 Width, uint32 Height)
|
||||
: mTexelFormat(ETexelFormat::RGBA8)
|
||||
, mSourceTexelFormat(ETexelFormat::RGBA8)
|
||||
, mWidth((uint16) Width)
|
||||
, mHeight((uint16) Height)
|
||||
, mNumMipMaps(1)
|
||||
, mLinearSize(Width * Height * 4)
|
||||
, mEnableMultisampling(false)
|
||||
, mBufferExists(false)
|
||||
, mpImgDataBuffer(nullptr)
|
||||
, mImgDataSize(0)
|
||||
, mGLBufferExists(false)
|
||||
{
|
||||
}
|
||||
|
||||
CTexture::~CTexture()
|
||||
{
|
||||
DeleteBuffers();
|
||||
}
|
||||
|
||||
bool CTexture::BufferGL()
|
||||
{
|
||||
GLenum BindTarget = (mEnableMultisampling ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
|
||||
glGenTextures(1, &mTextureID);
|
||||
glBindTexture(BindTarget, mTextureID);
|
||||
|
||||
GLenum GLFormat, GLType;
|
||||
bool IsCompressed = false;
|
||||
|
||||
switch (mTexelFormat)
|
||||
{
|
||||
case ETexelFormat::Luminance:
|
||||
GLFormat = GL_LUMINANCE;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case ETexelFormat::LuminanceAlpha:
|
||||
GLFormat = GL_LUMINANCE_ALPHA;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case ETexelFormat::RGB565:
|
||||
GLFormat = GL_RGB;
|
||||
GLType = GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
case ETexelFormat::RGBA4:
|
||||
GLFormat = GL_RGBA;
|
||||
GLType = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
break;
|
||||
case ETexelFormat::RGBA8:
|
||||
GLFormat = GL_RGBA;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case ETexelFormat::DXT1:
|
||||
GLFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
IsCompressed = true;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// The smallest mipmaps are probably not being loaded correctly, because mipmaps in GX textures have a minimum size depending on the format, and these don't.
|
||||
// Not sure specifically what accomodations should be made to fix that though so whatever.
|
||||
uint32 MipSize = mLinearSize;
|
||||
uint32 MipOffset = 0;
|
||||
uint16 MipW = mWidth, MipH = mHeight;
|
||||
|
||||
for (uint32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
GLvoid *pData = (mBufferExists) ? (mpImgDataBuffer + MipOffset) : NULL;
|
||||
|
||||
if (!IsCompressed)
|
||||
{
|
||||
if (mEnableMultisampling)
|
||||
glTexImage2DMultisample(BindTarget, 4, GLFormat, MipW, MipH, true);
|
||||
else
|
||||
glTexImage2D(BindTarget, iMip, GLFormat, MipW, MipH, 0, GLFormat, GLType, pData);
|
||||
}
|
||||
else
|
||||
glCompressedTexImage2D(BindTarget, iMip, GLFormat, MipW, MipH, 0, MipSize, pData);
|
||||
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
MipOffset += MipSize;
|
||||
MipSize /= 4;
|
||||
}
|
||||
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_MAX_LEVEL, mNumMipMaps - 1);
|
||||
|
||||
// Linear filtering on mipmaps:
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
|
||||
// Anisotropic filtering:
|
||||
float MaxAnisotropy;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxAnisotropy);
|
||||
glTexParameterf(BindTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, MaxAnisotropy);
|
||||
|
||||
mGLBufferExists = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTexture::Bind(uint32 GLTextureUnit)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + GLTextureUnit);
|
||||
|
||||
if (!mGLBufferExists)
|
||||
BufferGL();
|
||||
|
||||
GLenum BindTarget = (mEnableMultisampling ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
|
||||
glBindTexture(BindTarget, mTextureID);
|
||||
}
|
||||
|
||||
void CTexture::Resize(uint32 Width, uint32 Height)
|
||||
{
|
||||
if ((mWidth != Width) || (mHeight != Height))
|
||||
{
|
||||
DeleteBuffers();
|
||||
mWidth = (uint16) Width;
|
||||
mHeight = (uint16) Height;
|
||||
mNumMipMaps = 1;
|
||||
CalcLinearSize();
|
||||
}
|
||||
}
|
||||
|
||||
float CTexture::ReadTexelAlpha(const CVector2f& rkTexCoord)
|
||||
{
|
||||
// todo: support texel formats other than DXT1
|
||||
// DXT1 is definitely the most complicated one anyway; try reusing CTextureDecoder functions for other formats
|
||||
uint32 TexelX = (uint32) ((mWidth - 1) * rkTexCoord.X);
|
||||
uint32 TexelY = (uint32) ((mHeight - 1) * (1.f - std::fmod(rkTexCoord.Y, 1.f)));
|
||||
|
||||
if (mTexelFormat == ETexelFormat::DXT1 && mBufferExists)
|
||||
{
|
||||
CMemoryInStream Buffer(mpImgDataBuffer, mImgDataSize, EEndian::SystemEndian);
|
||||
|
||||
// 8 bytes per 4x4 16-pixel block, left-to-right top-to-bottom
|
||||
uint32 BlockIdxX = TexelX / 4;
|
||||
uint32 BlockIdxY = TexelY / 4;
|
||||
uint32 BlocksPerRow = mWidth / 4;
|
||||
|
||||
uint32 BufferPos = (8 * BlockIdxX) + (8 * BlockIdxY * BlocksPerRow);
|
||||
Buffer.Seek(BufferPos, SEEK_SET);
|
||||
|
||||
uint16 PaletteA = Buffer.ReadShort();
|
||||
uint16 PaletteB = Buffer.ReadShort();
|
||||
|
||||
if (PaletteA > PaletteB)
|
||||
{
|
||||
// No palette colors have alpha
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
// We only care about alpha, which is only present on palette index 3.
|
||||
// We don't need to calculate/decode the actual palette colors.
|
||||
uint32 BlockCol = (TexelX & 0xF) / 4;
|
||||
uint32 BlockRow = (TexelY & 0xF) / 4;
|
||||
|
||||
Buffer.Seek(BlockRow, SEEK_CUR);
|
||||
uint8 Row = Buffer.ReadByte();
|
||||
uint8 Shift = (uint8) (6 - (BlockCol * 2));
|
||||
uint8 PaletteIndex = (Row >> Shift) & 0x3;
|
||||
return (PaletteIndex == 3 ? 0.f : 1.f);
|
||||
}
|
||||
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
bool CTexture::WriteDDS(IOutputStream& rOut)
|
||||
{
|
||||
if (!rOut.IsValid()) return false;
|
||||
|
||||
CopyGLBuffer();
|
||||
|
||||
rOut.WriteFourCC(FOURCC('DDS ')); // "DDS " fourCC
|
||||
rOut.WriteLong(0x7C); // dwSize
|
||||
rOut.WriteLong(0x21007); // dwFlags
|
||||
rOut.WriteLong(mHeight); // dwHeight
|
||||
rOut.WriteLong(mWidth); // dwWidth
|
||||
rOut.WriteLong(mLinearSize); // dwPitchOrLinearSize
|
||||
rOut.WriteLong(0); // dwDepth
|
||||
rOut.WriteLong(mNumMipMaps - 1); // dwMipMapCount
|
||||
|
||||
for (uint32 iRes = 0; iRes < 11; iRes++)
|
||||
rOut.WriteLong(0); // dwReserved1[11]
|
||||
|
||||
// DDS_PIXELFORMAT
|
||||
rOut.WriteLong(32); // DDS_PIXELFORMAT.dwSize
|
||||
uint32 PFFlags = 0, PFBpp = 0, PFRBitMask = 0, PFGBitMask = 0, PFBBitMask = 0, PFABitMask = 0;
|
||||
|
||||
switch (mTexelFormat)
|
||||
{
|
||||
case ETexelFormat::Luminance:
|
||||
PFFlags = 0x20000;
|
||||
PFBpp = 0x8;
|
||||
PFRBitMask = 0xFF;
|
||||
break;
|
||||
case ETexelFormat::LuminanceAlpha:
|
||||
PFFlags = 0x20001;
|
||||
PFBpp = 0x10;
|
||||
PFRBitMask = 0x00FF;
|
||||
PFABitMask = 0xFF00;
|
||||
break;
|
||||
case ETexelFormat::RGBA4:
|
||||
PFFlags = 0x41;
|
||||
PFBpp = 0x10;
|
||||
PFRBitMask = 0x0F00;
|
||||
PFGBitMask = 0x00F0;
|
||||
PFBBitMask = 0x000F;
|
||||
PFABitMask = 0xF000;
|
||||
break;
|
||||
case ETexelFormat::RGB565:
|
||||
PFFlags = 0x40;
|
||||
PFBpp = 0x10;
|
||||
PFRBitMask = 0xF800;
|
||||
PFGBitMask = 0x7E0;
|
||||
PFBBitMask = 0x1F;
|
||||
break;
|
||||
case ETexelFormat::RGBA8:
|
||||
PFFlags = 0x41;
|
||||
PFBpp = 0x20;
|
||||
PFRBitMask = 0x00FF0000;
|
||||
PFGBitMask = 0x0000FF00;
|
||||
PFBBitMask = 0x000000FF;
|
||||
PFABitMask = 0xFF000000;
|
||||
break;
|
||||
case ETexelFormat::DXT1:
|
||||
PFFlags = 0x4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
rOut.WriteLong(PFFlags); // DDS_PIXELFORMAT.dwFlags
|
||||
(mTexelFormat == ETexelFormat::DXT1) ? rOut.WriteFourCC(FOURCC('DXT1')) : rOut.WriteLong(0); // DDS_PIXELFORMAT.dwFourCC
|
||||
rOut.WriteLong(PFBpp); // DDS_PIXELFORMAT.dwRGBBitCount
|
||||
rOut.WriteLong(PFRBitMask); // DDS_PIXELFORMAT.dwRBitMask
|
||||
rOut.WriteLong(PFGBitMask); // DDS_PIXELFORMAT.dwGBitMask
|
||||
rOut.WriteLong(PFBBitMask); // DDS_PIXELFORMAT.dwBBitMask
|
||||
rOut.WriteLong(PFABitMask); // DDS_PIXELFORMAT.dwABitMask
|
||||
|
||||
rOut.WriteLong(0x401000); // dwCaps
|
||||
rOut.WriteLong(0); // dwCaps2
|
||||
rOut.WriteLong(0); // dwCaps3
|
||||
rOut.WriteLong(0); // dwCaps4
|
||||
rOut.WriteLong(0); // dwReserved2
|
||||
|
||||
rOut.WriteBytes(mpImgDataBuffer, mImgDataSize); // Image data
|
||||
return true;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
uint32 CTexture::FormatBPP(ETexelFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case ETexelFormat::GX_I4: return 4;
|
||||
case ETexelFormat::GX_I8: return 8;
|
||||
case ETexelFormat::GX_IA4: return 8;
|
||||
case ETexelFormat::GX_IA8: return 16;
|
||||
case ETexelFormat::GX_C4: return 4;
|
||||
case ETexelFormat::GX_C8: return 8;
|
||||
case ETexelFormat::GX_RGB565: return 16;
|
||||
case ETexelFormat::GX_RGB5A3: return 16;
|
||||
case ETexelFormat::GX_RGBA8: return 32;
|
||||
case ETexelFormat::GX_CMPR: return 4;
|
||||
case ETexelFormat::Luminance: return 8;
|
||||
case ETexelFormat::LuminanceAlpha: return 16;
|
||||
case ETexelFormat::RGBA4: return 16;
|
||||
case ETexelFormat::RGB565: return 16;
|
||||
case ETexelFormat::RGBA8: return 32;
|
||||
case ETexelFormat::DXT1: return 4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ PRIVATE ************
|
||||
void CTexture::CalcLinearSize()
|
||||
{
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
mLinearSize = (uint32) (mWidth * mHeight * BytesPerPixel);
|
||||
}
|
||||
|
||||
uint32 CTexture::CalcTotalSize()
|
||||
{
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
uint32 MipW = mWidth, MipH = mHeight;
|
||||
uint32 Size = 0;
|
||||
|
||||
for (uint32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
Size += (uint32) (MipW * MipH * BytesPerPixel);
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
}
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
void CTexture::CopyGLBuffer()
|
||||
{
|
||||
if (!mGLBufferExists) return;
|
||||
|
||||
// Clear existing buffer
|
||||
if (mBufferExists)
|
||||
{
|
||||
delete[] mpImgDataBuffer;
|
||||
mBufferExists = false;
|
||||
mpImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
}
|
||||
|
||||
// Calculate buffer size
|
||||
mImgDataSize = CalcTotalSize();
|
||||
mpImgDataBuffer = new uint8[mImgDataSize];
|
||||
mBufferExists = true;
|
||||
|
||||
// Get texture
|
||||
uint32 MipW = mWidth, MipH = mHeight, MipOffset = 0;
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
|
||||
GLenum BindTarget = (mEnableMultisampling ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
|
||||
glBindTexture(BindTarget, mTextureID);
|
||||
|
||||
for (uint32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
void *pData = mpImgDataBuffer + MipOffset;
|
||||
|
||||
glGetTexImage(BindTarget, iMip, GL_RGBA, GL_UNSIGNED_BYTE, pData);
|
||||
|
||||
MipOffset += (uint32) (MipW * MipH * BytesPerPixel);
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
}
|
||||
|
||||
mTexelFormat = ETexelFormat::RGBA8;
|
||||
mLinearSize = mWidth * mHeight * 4;
|
||||
}
|
||||
|
||||
void CTexture::DeleteBuffers()
|
||||
{
|
||||
if (mBufferExists)
|
||||
{
|
||||
delete[] mpImgDataBuffer;
|
||||
mBufferExists = false;
|
||||
mpImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
}
|
||||
|
||||
if (mGLBufferExists)
|
||||
{
|
||||
glDeleteTextures(1, &mTextureID);
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
#ifndef CTEXTURE_H
|
||||
#define CTEXTURE_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "ETexelFormat.h"
|
||||
#include <Common/BasicTypes.h>
|
||||
#include <Common/FileIO.h>
|
||||
#include <Common/Math/CVector2f.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
class CTexture : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(Texture)
|
||||
friend class CTextureDecoder;
|
||||
friend class CTextureEncoder;
|
||||
|
||||
ETexelFormat mTexelFormat; // Format of decoded image data
|
||||
ETexelFormat mSourceTexelFormat; // Format of input TXTR file
|
||||
uint16 mWidth, mHeight; // Image dimensions
|
||||
uint32 mNumMipMaps; // The number of mipmaps this texture has
|
||||
uint32 mLinearSize; // The size of the top level mipmap, in bytes
|
||||
|
||||
bool mEnableMultisampling; // Whether multisample should be enabled (if this texture is a render target).
|
||||
bool mBufferExists; // Indicates whether image data buffer has valid data
|
||||
uint8 *mpImgDataBuffer; // Pointer to image data buffer
|
||||
uint32 mImgDataSize; // Size of image data buffer
|
||||
|
||||
bool mGLBufferExists; // Indicates whether GL buffer has valid data
|
||||
GLuint mTextureID; // ID for texture GL buffer
|
||||
|
||||
public:
|
||||
CTexture(CResourceEntry *pEntry = 0);
|
||||
CTexture(uint32 Width, uint32 Height);
|
||||
~CTexture();
|
||||
|
||||
bool BufferGL();
|
||||
void Bind(uint32 GLTextureUnit);
|
||||
void Resize(uint32 Width, uint32 Height);
|
||||
float ReadTexelAlpha(const CVector2f& rkTexCoord);
|
||||
bool WriteDDS(IOutputStream& rOut);
|
||||
|
||||
// Accessors
|
||||
ETexelFormat TexelFormat() const { return mTexelFormat; }
|
||||
ETexelFormat SourceTexelFormat() const { return mSourceTexelFormat; }
|
||||
uint32 Width() const { return (uint32) mWidth; }
|
||||
uint32 Height() const { return (uint32) mHeight; }
|
||||
uint32 NumMipMaps() const { return mNumMipMaps; }
|
||||
GLuint TextureID() const { return mTextureID; }
|
||||
|
||||
inline void SetMultisamplingEnabled(bool Enable)
|
||||
{
|
||||
if (mEnableMultisampling != Enable)
|
||||
DeleteBuffers();
|
||||
|
||||
mEnableMultisampling = Enable;
|
||||
}
|
||||
|
||||
// Static
|
||||
static uint32 FormatBPP(ETexelFormat Format);
|
||||
|
||||
// Private
|
||||
private:
|
||||
void CalcLinearSize();
|
||||
uint32 CalcTotalSize();
|
||||
void CopyGLBuffer();
|
||||
void DeleteBuffers();
|
||||
};
|
||||
|
||||
#endif // CTEXTURE_H
|
|
@ -1,3 +1,4 @@
|
|||
#if 0
|
||||
#include "CTextureEncoder.h"
|
||||
#include <Common/Log.h>
|
||||
|
||||
|
@ -10,12 +11,12 @@ void CTextureEncoder::WriteTXTR(IOutputStream& rTXTR)
|
|||
{
|
||||
// Only DXT1->CMPR supported at the moment
|
||||
rTXTR.WriteLong((uint) mOutputFormat);
|
||||
rTXTR.WriteShort(mpTexture->mWidth);
|
||||
rTXTR.WriteShort(mpTexture->mHeight);
|
||||
rTXTR.WriteLong(mpTexture->mNumMipMaps);
|
||||
|
||||
uint32 MipW = mpTexture->Width() / 4;
|
||||
uint32 MipH = mpTexture->Height() / 4;
|
||||
rTXTR.WriteShort(mpTexture->SizeX());
|
||||
rTXTR.WriteShort(mpTexture->SizeY());
|
||||
rTXTR.WriteLong(mpTexture->NumMipMaps());
|
||||
/*
|
||||
uint32 MipW = mpTexture->SizeX() / 4;
|
||||
uint32 MipH = mpTexture->SizeY() / 4;
|
||||
CMemoryInStream Image(mpTexture->mpImgDataBuffer, mpTexture->mImgDataSize, EEndian::LittleEndian);
|
||||
uint32 MipOffset = Image.Tell();
|
||||
|
||||
|
@ -37,7 +38,7 @@ void CTextureEncoder::WriteTXTR(IOutputStream& rTXTR)
|
|||
MipH /= 2;
|
||||
if (MipW < 2) MipW = 2;
|
||||
if (MipH < 2) MipH = 2;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void CTextureEncoder::DetermineBestOutputFormat()
|
||||
|
@ -61,7 +62,7 @@ void CTextureEncoder::ReadSubBlockCMPR(IInputStream& rSource, IOutputStream& rDe
|
|||
// ************ STATIC ************
|
||||
void CTextureEncoder::EncodeTXTR(IOutputStream& rTXTR, CTexture *pTex)
|
||||
{
|
||||
if (pTex->mTexelFormat != ETexelFormat::DXT1)
|
||||
if (pTex->mEditorFormat != ETexelFormat::BC1)
|
||||
{
|
||||
errorf("Unsupported texel format for decoding");
|
||||
return;
|
||||
|
@ -69,7 +70,7 @@ void CTextureEncoder::EncodeTXTR(IOutputStream& rTXTR, CTexture *pTex)
|
|||
|
||||
CTextureEncoder Encoder;
|
||||
Encoder.mpTexture = pTex;
|
||||
Encoder.mSourceFormat = ETexelFormat::DXT1;
|
||||
Encoder.mSourceFormat = ETexelFormat::BC1;
|
||||
Encoder.mOutputFormat = ETexelFormat::GX_CMPR;
|
||||
Encoder.WriteTXTR(rTXTR);
|
||||
}
|
||||
|
@ -86,10 +87,9 @@ ETexelFormat CTextureEncoder::GetGXFormat(ETexelFormat Format)
|
|||
{
|
||||
case ETexelFormat::Luminance: return ETexelFormat::GX_I8;
|
||||
case ETexelFormat::LuminanceAlpha: return ETexelFormat::GX_IA8;
|
||||
case ETexelFormat::RGBA4: return ETexelFormat::GX_RGB5A3;
|
||||
case ETexelFormat::RGB565: return ETexelFormat::GX_RGB565;
|
||||
case ETexelFormat::RGBA8: return ETexelFormat::GX_RGBA8;
|
||||
case ETexelFormat::DXT1: return ETexelFormat::GX_CMPR;
|
||||
case ETexelFormat::BC1: return ETexelFormat::GX_CMPR;
|
||||
default: return ETexelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,8 @@ ETexelFormat CTextureEncoder::GetFormat(ETexelFormat Format)
|
|||
case ETexelFormat::GX_IA4: return ETexelFormat::LuminanceAlpha;
|
||||
case ETexelFormat::GX_IA8: return ETexelFormat::LuminanceAlpha;
|
||||
// todo rest of these
|
||||
case ETexelFormat::GX_CMPR: return ETexelFormat::DXT1;
|
||||
case ETexelFormat::GX_CMPR: return ETexelFormat::BC1;
|
||||
default: return ETexelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef CTEXTUREENCODER_H
|
||||
#define CTEXTUREENCODER_H
|
||||
|
||||
#include "Core/Resource/CTexture.h"
|
||||
#if 0
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
#include "Core/Resource/TResPtr.h"
|
||||
|
||||
// Class contains basic functionality right now - only supports directly converting DXT1 to CMPR
|
||||
|
@ -23,5 +24,6 @@ public:
|
|||
static ETexelFormat GetGXFormat(ETexelFormat Format);
|
||||
static ETexelFormat GetFormat(ETexelFormat Format);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // CTEXTUREENCODER_H
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
#ifndef ETEXELFORMAT
|
||||
#define ETEXELFORMAT
|
||||
|
||||
// ETexelFormat - supported internal formats for decoded textures
|
||||
enum class ETexelFormat
|
||||
{
|
||||
// Supported texel formats in GX using Retro's numbering
|
||||
GX_I4 = 0x0,
|
||||
GX_I8 = 0x1,
|
||||
GX_IA4 = 0x2,
|
||||
GX_IA8 = 0x3,
|
||||
GX_C4 = 0x4,
|
||||
GX_C8 = 0x5,
|
||||
GX_C14x2 = 0x6,
|
||||
GX_RGB565 = 0x7,
|
||||
GX_RGB5A3 = 0x8,
|
||||
GX_RGBA8 = 0x9,
|
||||
GX_CMPR = 0xA,
|
||||
// Supported internal texel formats for decoded textures
|
||||
Luminance,
|
||||
LuminanceAlpha,
|
||||
RGBA4,
|
||||
RGB565,
|
||||
RGBA8,
|
||||
DXT1,
|
||||
Invalid = -1
|
||||
};
|
||||
|
||||
// EGXPaletteFormat - GX's supported palette texel formats for C4/C8
|
||||
enum class EGXPaletteFormat
|
||||
{
|
||||
IA8 = 0,
|
||||
RGB565 = 1,
|
||||
RGB5A3 = 2
|
||||
};
|
||||
|
||||
#endif // ETEXELFORMAT
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,8 +1,8 @@
|
|||
#ifndef CTEXTUREDECODER_H
|
||||
#define CTEXTUREDECODER_H
|
||||
|
||||
#include "Core/Resource/CTexture.h"
|
||||
#include "Core/Resource/ETexelFormat.h"
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
#include "Core/Resource/Texture/ETexelFormat.h"
|
||||
#include <Common/BasicTypes.h>
|
||||
#include <Common/CColor.h>
|
||||
|
||||
|
@ -10,86 +10,44 @@
|
|||
|
||||
class CTextureDecoder
|
||||
{
|
||||
CResourceEntry *mpEntry;
|
||||
ETexelFormat mTexelFormat;
|
||||
uint16 mWidth, mHeight;
|
||||
CResourceEntry* mpEntry;
|
||||
CTexture* mpTexture;
|
||||
|
||||
// Texture asset data
|
||||
EGXTexelFormat mTexelFormat;
|
||||
uint16 mSizeX, mSizeY;
|
||||
uint32 mNumMipMaps;
|
||||
|
||||
bool mHasPalettes;
|
||||
EGXPaletteFormat mPaletteFormat;
|
||||
std::vector<uint8> mPalettes;
|
||||
CMemoryInStream mPaletteInput;
|
||||
|
||||
struct SDDSInfo
|
||||
{
|
||||
enum { DXT1, DXT2, DXT3, DXT4, DXT5, RGBA } Format;
|
||||
uint32 Flags;
|
||||
uint32 BitCount;
|
||||
uint32 RBitMask, GBitMask, BBitMask, ABitMask;
|
||||
uint32 RShift, GShift, BShift, AShift;
|
||||
uint32 RSize, GSize, BSize, ASize;
|
||||
} mDDSInfo;
|
||||
|
||||
uint8 *mpDataBuffer;
|
||||
uint32 mDataBufferSize;
|
||||
|
||||
// Private Functions
|
||||
CTextureDecoder();
|
||||
~CTextureDecoder();
|
||||
CTexture* CreateTexture();
|
||||
|
||||
// Read
|
||||
void ReadTXTR(IInputStream& rTXTR);
|
||||
void ReadDDS(IInputStream& rDDS);
|
||||
// Palette data
|
||||
std::vector<uint8> mPaletteData;
|
||||
uint32 mPaletteTexelStride;
|
||||
|
||||
// Decode
|
||||
void PartialDecodeGXTexture(IInputStream& rTXTR);
|
||||
void FullDecodeGXTexture(IInputStream& rTXTR);
|
||||
void DecodeDDS(IInputStream& rDDS);
|
||||
void DecodeGXTexture(IInputStream& TXTR);
|
||||
void ParseTexel(EGXTexelFormat Format, IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseI4(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseI8(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseIA4(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseIA8(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseC4(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseC8(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseRGB565(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseRGB5A3(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseRGBA8(IInputStream& Src, IOutputStream& Dst);
|
||||
void ParseCMPR(IInputStream& Src, IOutputStream& Dst);
|
||||
|
||||
// Decode Pixels (preserve compression)
|
||||
void ReadPixelsI4(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadPixelI8(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadPixelIA4(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadPixelIA8(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadPixelsC4(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadPixelC8(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadPixelRGB565(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadPixelRGB5A3(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadPixelRGBA8(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void ReadSubBlockCMPR(IInputStream& rSrc, IOutputStream& rDst);
|
||||
CTextureDecoder(CResourceEntry* pEntry);
|
||||
~CTextureDecoder();
|
||||
CTexture* ReadTXTR(IInputStream& TXTR);
|
||||
|
||||
// Decode Pixels (convert to RGBA8)
|
||||
CColor DecodePixelI4(uint8 Byte, uint8 WhichPixel);
|
||||
CColor DecodePixelI8(uint8 Byte);
|
||||
CColor DecodePixelIA4(uint8 Byte);
|
||||
CColor DecodePixelIA8(uint16 Short);
|
||||
CColor DecodePixelC4(uint8 Byte, uint8 WhichPixel, IInputStream& rPaletteStream);
|
||||
CColor DecodePixelC8(uint8 Byte, IInputStream& rPaletteStream);
|
||||
CColor DecodePixelRGB565(uint16 Short);
|
||||
CColor DecodePixelRGB5A3(uint16 Short);
|
||||
CColor DecodePixelRGBA8(IInputStream& rSrc, IOutputStream& rDst);
|
||||
void DecodeSubBlockCMPR(IInputStream& rSrc, IOutputStream& rDst, uint16 Width);
|
||||
|
||||
void DecodeBlockBC1(IInputStream& rSrc, IOutputStream& rDst, uint32 Width);
|
||||
void DecodeBlockBC2(IInputStream& rSrc, IOutputStream& rDst, uint32 Width);
|
||||
void DecodeBlockBC3(IInputStream& rSrc, IOutputStream& rDst, uint32 Width);
|
||||
CColor DecodeDDSPixel(IInputStream& rDDS);
|
||||
|
||||
// Static
|
||||
public:
|
||||
static CTexture* LoadTXTR(IInputStream& rTXTR, CResourceEntry *pEntry);
|
||||
static CTexture* LoadDDS(IInputStream& rDDS, CResourceEntry *pEntry);
|
||||
static CTexture* DoFullDecode(IInputStream& rTXTR, CResourceEntry *pEntry);
|
||||
static CTexture* DoFullDecode(CTexture *pTexture);
|
||||
static CTexture* LoadTXTR(IInputStream& TXTR, CResourceEntry* pEntry);
|
||||
|
||||
// Utility
|
||||
static uint8 Extend3to8(uint8 In);
|
||||
static uint8 Extend4to8(uint8 In);
|
||||
static uint8 Extend5to8(uint8 In);
|
||||
static uint8 Extend6to8(uint8 In);
|
||||
static uint32 CalculateShiftForMask(uint32 BitMask);
|
||||
static uint32 CalculateMaskBitCount(uint32 BitMask);
|
||||
};
|
||||
|
||||
#endif // CTEXTUREDECODER_H
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
#ifndef RESOURCES_H
|
||||
#define RESOURCES_H
|
||||
|
||||
#include "CAudioGroup.h"
|
||||
#include "CAudioLookupTable.h"
|
||||
#include "CAudioMacro.h"
|
||||
#include "CDependencyGroup.h"
|
||||
#include "CFont.h"
|
||||
#include "CMapArea.h"
|
||||
#include "CPoiToWorld.h"
|
||||
#include "CResource.h"
|
||||
#include "CTexture.h"
|
||||
#include "CStringList.h"
|
||||
#include "CWorld.h"
|
||||
#include "Core/Resource/Animation/CAnimation.h"
|
||||
#include "Core/Resource/Animation/CAnimSet.h"
|
||||
|
@ -16,6 +20,7 @@
|
|||
#include "Core/Resource/Model/CModel.h"
|
||||
#include "Core/Resource/Scan/CScan.h"
|
||||
#include "Core/Resource/StringTable/CStringTable.h"
|
||||
#include "Core/Resource/Texture/CTexture.h"
|
||||
|
||||
#endif // RESOURCES_H
|
||||
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
#include "CTexture.h"
|
||||
#include "NTextureUtils.h"
|
||||
#include <Common/Math/MathUtil.h>
|
||||
|
||||
CTexture::CTexture(CResourceEntry *pEntry /*= 0*/)
|
||||
: CResource(pEntry)
|
||||
, mEditorFormat(ETexelFormat::RGBA8)
|
||||
, mGameFormat(EGXTexelFormat::RGBA8)
|
||||
, mEnableMultisampling(false)
|
||||
, mTextureResource(0)
|
||||
{
|
||||
}
|
||||
|
||||
CTexture::CTexture(uint32 SizeX, uint32 SizeY)
|
||||
: mEditorFormat(ETexelFormat::RGBA8)
|
||||
, mGameFormat(EGXTexelFormat::RGBA8)
|
||||
, mEnableMultisampling(false)
|
||||
, mTextureResource(0)
|
||||
{
|
||||
mMipData.emplace_back();
|
||||
SMipData& Mip = mMipData.back();
|
||||
Mip.SizeX = SizeX;
|
||||
Mip.SizeY = SizeY;
|
||||
Mip.DataBuffer.resize(SizeX * SizeY * 4);
|
||||
}
|
||||
|
||||
CTexture::~CTexture()
|
||||
{
|
||||
ReleaseRenderResources();
|
||||
}
|
||||
|
||||
void CTexture::CreateRenderResources()
|
||||
{
|
||||
if (mTextureResource != 0)
|
||||
{
|
||||
ReleaseRenderResources();
|
||||
}
|
||||
|
||||
GLenum BindTarget = (mEnableMultisampling ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
|
||||
glGenTextures(1, &mTextureResource);
|
||||
glBindTexture(BindTarget, mTextureResource);
|
||||
|
||||
GLenum GLFormat, GLType;
|
||||
|
||||
switch (mEditorFormat)
|
||||
{
|
||||
case ETexelFormat::Luminance:
|
||||
GLFormat = GL_R8;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case ETexelFormat::LuminanceAlpha:
|
||||
GLFormat = GL_RG8;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case ETexelFormat::RGB565:
|
||||
GLFormat = GL_RGB;
|
||||
GLType = GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
case ETexelFormat::RGBA8:
|
||||
GLFormat = GL_RGBA;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
}
|
||||
|
||||
// The smallest mipmaps are probably not being loaded correctly, because mipmaps in GX textures have a minimum size depending on the format, and these don't.
|
||||
// Not sure specifically what accomodations should be made to fix that though so whatever.
|
||||
for (uint MipIdx = 0; MipIdx < mMipData.size(); MipIdx++)
|
||||
{
|
||||
const SMipData& MipData = mMipData[MipIdx];
|
||||
uint SizeX = MipData.SizeX;
|
||||
uint SizeY = MipData.SizeY;
|
||||
const void* pkData = MipData.DataBuffer.data();
|
||||
|
||||
if (mEnableMultisampling)
|
||||
{
|
||||
glTexImage2DMultisample(BindTarget, 4, GLFormat, SizeX, SizeY, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexImage2D(BindTarget, MipIdx, GLFormat, SizeX, SizeY, 0, GLFormat, GLType, pkData);
|
||||
}
|
||||
}
|
||||
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_MAX_LEVEL, mMipData.size() - 1);
|
||||
|
||||
// Linear filtering on mipmaps:
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
|
||||
// Anisotropic filtering:
|
||||
float MaxAnisotropy;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxAnisotropy);
|
||||
glTexParameterf(BindTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, MaxAnisotropy);
|
||||
|
||||
// Swizzle for luminance formats
|
||||
if (mEditorFormat == ETexelFormat::Luminance)
|
||||
{
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_SWIZZLE_RGBA, GL_RED);
|
||||
}
|
||||
else if (mEditorFormat == ETexelFormat::LuminanceAlpha)
|
||||
{
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_SWIZZLE_R, GL_RED);
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_SWIZZLE_G, GL_RED);
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
glTexParameteri(BindTarget, GL_TEXTURE_SWIZZLE_A, GL_GREEN);
|
||||
}
|
||||
}
|
||||
|
||||
void CTexture::ReleaseRenderResources()
|
||||
{
|
||||
if (mTextureResource != 0)
|
||||
{
|
||||
glDeleteTextures(1, &mTextureResource);
|
||||
mTextureResource = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CTexture::BindToSampler(uint SamplerIndex) const
|
||||
{
|
||||
// CreateGraphicsResources() must have been called before calling this
|
||||
// @todo this should not be the responsibility of CTexture
|
||||
ASSERT( mTextureResource != 0 );
|
||||
GLenum BindTarget = (mEnableMultisampling ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + SamplerIndex);
|
||||
glBindTexture(BindTarget, mTextureResource);
|
||||
}
|
||||
|
||||
/** Generate mipmap chain based on the contents of the first mip */
|
||||
void CTexture::GenerateMipTail(uint NumMips /*= 0*/)
|
||||
{
|
||||
//@todo
|
||||
}
|
||||
|
||||
/** Allocate mipmap data, but does not fill any data. Returns the new mipmap count. */
|
||||
uint CTexture::AllocateMipTail(uint DesiredMipCount /*= 0*/)
|
||||
{
|
||||
// We must have at least one mipmap to start with.
|
||||
if (mMipData.empty())
|
||||
{
|
||||
warnf("Failed to allocate mip tail; texture is empty, did not initialize correctly");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try to allocate the requested number of mipmaps, but don't allocate any below 1x1.
|
||||
// Also, we always need at least one mipmap.
|
||||
uint BaseSizeX = mMipData[0].SizeX;
|
||||
uint BaseSizeY = mMipData[0].SizeY;
|
||||
uint MaxMips = Math::Min( Math::FloorLog2(BaseSizeX), Math::FloorLog2(BaseSizeY) ) + 1;
|
||||
uint NewMipCount = Math::Min(MaxMips, DesiredMipCount);
|
||||
|
||||
if (mMipData.size() != NewMipCount)
|
||||
{
|
||||
uint OldMipCount = mMipData.size();
|
||||
mMipData.resize(NewMipCount);
|
||||
|
||||
// Allocate internal data for any new mips.
|
||||
if (NewMipCount > OldMipCount)
|
||||
{
|
||||
uint LastMipIdx = OldMipCount - 1;
|
||||
uint SizeX = mMipData[LastMipIdx].SizeX;
|
||||
uint SizeY = mMipData[LastMipIdx].SizeY;
|
||||
uint BPP = NTextureUtils::GetTexelFormatInfo( mEditorFormat ).BitsPerPixel;
|
||||
|
||||
for (uint MipIdx = OldMipCount; MipIdx < NewMipCount; MipIdx++)
|
||||
{
|
||||
SizeX /= 2;
|
||||
SizeY /= 2;
|
||||
uint Size = (SizeX * SizeY * BPP) / 8;
|
||||
mMipData[MipIdx].SizeX = SizeX;
|
||||
mMipData[MipIdx].SizeY = SizeY;
|
||||
mMipData[MipIdx].DataBuffer.resize(Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mMipData.size();
|
||||
}
|
||||
|
||||
/** Compress the texture data into the format specified by Format. */
|
||||
void CTexture::Compress(EGXTexelFormat Format)
|
||||
{
|
||||
// @todo load source PNG data
|
||||
|
||||
}
|
||||
|
||||
/** Generate editor texel data based on the currently loaded game texel data */
|
||||
void CTexture::GenerateEditorData(bool bClearGameData /*= true*/)
|
||||
{
|
||||
for (uint MipIdx = 0; MipIdx < mMipData.size(); MipIdx++)
|
||||
{
|
||||
SMipData& MipData = mMipData[MipIdx];
|
||||
|
||||
NTextureUtils::ConvertGameDataToEditorData(
|
||||
mGameFormat,
|
||||
MipData.SizeX,
|
||||
MipData.SizeY,
|
||||
MipData.GameDataBuffer.data(),
|
||||
MipData.GameDataBuffer.size(),
|
||||
MipData.DataBuffer,
|
||||
MipIdx == 0 ? &mEditorFormat : nullptr
|
||||
);
|
||||
|
||||
if (bClearGameData)
|
||||
{
|
||||
MipData.GameDataBuffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the internal resolution of the texture; used for dynamically-scaling textures
|
||||
*/
|
||||
void CTexture::Resize(uint32 SizeX, uint32 SizeY)
|
||||
{
|
||||
if (mMipData.size() > 0)
|
||||
{
|
||||
if (mMipData[0].SizeX == SizeX &&
|
||||
mMipData[0].SizeY == SizeY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mMipData.size() == 0)
|
||||
{
|
||||
mMipData.emplace_back();
|
||||
}
|
||||
|
||||
const STexelFormatInfo& kFormatInfo = NTextureUtils::GetTexelFormatInfo(mEditorFormat);
|
||||
mMipData.back().SizeX = SizeX;
|
||||
mMipData.back().SizeY = SizeY;
|
||||
mMipData.back().DataBuffer.resize( SizeX * SizeY * kFormatInfo.BitsPerPixel / 8 );
|
||||
|
||||
if (mTextureResource != 0)
|
||||
{
|
||||
ReleaseRenderResources();
|
||||
CreateRenderResources();
|
||||
}
|
||||
}
|
||||
|
||||
float CTexture::ReadTexelAlpha(const CVector2f& kTexCoord)
|
||||
{
|
||||
// @todo: this is an inaccurate implementation because it
|
||||
// doesn't take into account mipmaps or texture filtering
|
||||
const SMipData& kMipData = mMipData[0];
|
||||
uint32 TexelX = (uint32) ((kMipData.SizeX - 1) * kTexCoord.X);
|
||||
uint32 TexelY = (uint32) ((kMipData.SizeY - 1) * (1.f - fmodf(kTexCoord.Y, 1.f)));
|
||||
|
||||
|
||||
if (mEditorFormat == ETexelFormat::Luminance || mEditorFormat == ETexelFormat::RGB565)
|
||||
{
|
||||
// No alpha in these formats
|
||||
return 1.f;
|
||||
}
|
||||
else if (mEditorFormat == ETexelFormat::LuminanceAlpha)
|
||||
{
|
||||
uint Offset = (TexelY * kMipData.SizeX * 2) + TexelX*2 + 1;
|
||||
uint8 Alpha = *((uint8*) &kMipData.DataBuffer[Offset]);
|
||||
return Alpha / 255.f;
|
||||
}
|
||||
else if (mEditorFormat == ETexelFormat::RGBA8)
|
||||
{
|
||||
uint Offset = (TexelY * kMipData.SizeX * 4) + TexelX*4 + 3;
|
||||
uint8 Alpha = *((uint8*) &kMipData.DataBuffer[Offset]);
|
||||
return Alpha / 255.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorf("Unhandled texel format in ReadTexelAlpha(): %s",
|
||||
TEnumReflection<ETexelFormat>::ConvertValueToString(mEditorFormat));
|
||||
return 1.f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
#ifndef CTEXTURE_H
|
||||
#define CTEXTURE_H
|
||||
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include "ETexelFormat.h"
|
||||
#include <Common/BasicTypes.h>
|
||||
#include <Common/FileIO.h>
|
||||
#include <Common/Math/CVector2f.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
/** Mipmap data */
|
||||
struct SMipData
|
||||
{
|
||||
/** Mip dimensions */
|
||||
uint SizeX, SizeY;
|
||||
|
||||
/** Mip texel data */
|
||||
std::vector<uint8> DataBuffer;
|
||||
|
||||
/** Mip texel data with game texel formats; may be empty when not in use */
|
||||
std::vector<uint8> GameDataBuffer;
|
||||
};
|
||||
|
||||
/** Class representing a 2D texture asset */
|
||||
class CTexture : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(Texture)
|
||||
friend class CTextureDecoder;
|
||||
friend class CTextureEncoder;
|
||||
|
||||
/** Format of encoded image data in-game */
|
||||
EGXTexelFormat mGameFormat;
|
||||
|
||||
/** Format of decoded image data in-editor */
|
||||
ETexelFormat mEditorFormat;
|
||||
|
||||
/** Mipmap data */
|
||||
std::vector<SMipData> mMipData;
|
||||
|
||||
/** @todo the following is OpenGL stuff that really shouldn't be implemented here */
|
||||
/** Whether multisample should be enabled (if this texture is a render target). */
|
||||
bool mEnableMultisampling;
|
||||
|
||||
/** OpenGL texture resource handle */
|
||||
GLuint mTextureResource;
|
||||
|
||||
public:
|
||||
/** Constructors */
|
||||
CTexture(CResourceEntry* pEntry = 0);
|
||||
CTexture(uint SizeX, uint SizeY);
|
||||
~CTexture();
|
||||
|
||||
/** Generate mipmap chain based on the contents of the first mip */
|
||||
void GenerateMipTail(uint NumMips = 0);
|
||||
|
||||
/** Allocate mipmap data, but does not fill any data. Returns the new mipmap count. */
|
||||
uint AllocateMipTail(uint DesiredMipCount = 0);
|
||||
|
||||
/** Compress the texture data into the format specified by Format. */
|
||||
void Compress(EGXTexelFormat Format);
|
||||
|
||||
/** Generate editor texel data based on the currently loaded game texel data */
|
||||
void GenerateEditorData(bool bClearGameData = true);
|
||||
|
||||
/**
|
||||
* Update the internal resolution of the texture; used for dynamically-scaling textures
|
||||
* @todo - should not be implemented here as these textures are not relevant to texture assets
|
||||
*/
|
||||
void Resize(uint SizeX, uint SizeY);
|
||||
|
||||
/** Return the alpha value of the texel at the given coordinates */
|
||||
float ReadTexelAlpha(const CVector2f& rkTexCoord);
|
||||
|
||||
/** Create/release resources for rendering this texture */
|
||||
void CreateRenderResources();
|
||||
void ReleaseRenderResources();
|
||||
|
||||
// Accessors
|
||||
FORCEINLINE ETexelFormat EditorTexelFormat() const { return mEditorFormat; }
|
||||
FORCEINLINE EGXTexelFormat GameTexelFormat() const { return mGameFormat; }
|
||||
FORCEINLINE uint SizeX() const { return mMipData.empty() ? 0 : mMipData[0].SizeX; }
|
||||
FORCEINLINE uint SizeY() const { return mMipData.empty() ? 0 : mMipData[0].SizeY; }
|
||||
FORCEINLINE uint NumMipMaps() const { return mMipData.size(); }
|
||||
FORCEINLINE GLuint RenderResource() const { return mTextureResource; }
|
||||
|
||||
FORCEINLINE SMipData& GetMipData(uint Idx) { return mMipData[Idx]; }
|
||||
|
||||
/** @todo these functions shouldn't be handled in this class. */
|
||||
void BindToSampler(uint SamplerIndex) const;
|
||||
|
||||
FORCEINLINE void SetMultisamplingEnabled(bool Enable)
|
||||
{
|
||||
mEnableMultisampling = Enable;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CTEXTURE_H
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef ETEXELFORMAT
|
||||
#define ETEXELFORMAT
|
||||
|
||||
#include <Common/BasicTypes.h>
|
||||
|
||||
/** Texel formats in GX using Retro's indexing from the TXTR format */
|
||||
enum class EGXTexelFormat
|
||||
{
|
||||
// 4-bit single-channel greyscale
|
||||
I4 = 0x0,
|
||||
// 8-bit single-channel greyscale
|
||||
I8 = 0x1,
|
||||
// 4-bit two-channel greyscale with alpha
|
||||
IA4 = 0x2,
|
||||
// 8-bit two-channel greyscale with alpha
|
||||
IA8 = 0x3,
|
||||
// Palette with 4-bit indices
|
||||
C4 = 0x4,
|
||||
// Palette with 8-bit indices
|
||||
C8 = 0x5,
|
||||
// Unused
|
||||
C14x2 = 0x6,
|
||||
// 16-bit three-channel RGB texture
|
||||
RGB565 = 0x7,
|
||||
// 16-bit four-channel RGBA texture
|
||||
RGB5A3 = 0x8,
|
||||
// 32-bit four-channel uncompressed RGBA texture
|
||||
RGBA8 = 0x9,
|
||||
// Compressed CMPR (similar to BC1/DXT1) RGBA texture with one-bit alpha
|
||||
CMPR = 0xA,
|
||||
|
||||
Invalid = -1
|
||||
};
|
||||
|
||||
/** Texel formats useed internally in the editor */
|
||||
enum class ETexelFormat
|
||||
{
|
||||
// Single-channel greyscale
|
||||
Luminance,
|
||||
// Two-channel greyscale with alpha
|
||||
LuminanceAlpha,
|
||||
// Three-channel 16-bit RGB texture
|
||||
RGB565,
|
||||
// Four-channel 32-bit uncompressed RGBA texture
|
||||
RGBA8,
|
||||
|
||||
Invalid = -1
|
||||
};
|
||||
|
||||
/** Info about texel formats; retrieve via NTextureUtils */
|
||||
struct STexelFormatInfo
|
||||
{
|
||||
EGXTexelFormat GameTexelFormat;
|
||||
ETexelFormat EditorTexelFormat;
|
||||
uint BlockSizeX;
|
||||
uint BlockSizeY;
|
||||
uint BitsPerPixel;
|
||||
};
|
||||
|
||||
#endif // ETEXELFORMAT
|
|
@ -0,0 +1,446 @@
|
|||
#include "NTextureUtils.h"
|
||||
#include <Common/Common.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ASSERT ASSERT
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
namespace NTextureUtils
|
||||
{
|
||||
|
||||
/** Table of image format info; indexed by EGXTexelFormat. */
|
||||
STexelFormatInfo kTexelFormatInfo[] =
|
||||
{
|
||||
// GameTexelFormat EditorTexelFormat BlockSizeX BlockSizeY BitsPerPixel
|
||||
{ EGXTexelFormat::I4, ETexelFormat::Luminance, 8, 8, 4 },
|
||||
{ EGXTexelFormat::I8, ETexelFormat::Luminance, 8, 4, 8 },
|
||||
{ EGXTexelFormat::IA4, ETexelFormat::LuminanceAlpha, 8, 4, 8 },
|
||||
{ EGXTexelFormat::IA8, ETexelFormat::LuminanceAlpha, 4, 4, 16 },
|
||||
{ EGXTexelFormat::C4, ETexelFormat::Invalid, 8, 8, 4 },
|
||||
{ EGXTexelFormat::C8, ETexelFormat::Invalid, 8, 4, 8 },
|
||||
{ EGXTexelFormat::C14x2, ETexelFormat::Invalid, 4, 4, 16 },
|
||||
{ EGXTexelFormat::RGB565, ETexelFormat::RGB565, 4, 4, 16 },
|
||||
{ EGXTexelFormat::RGB5A3, ETexelFormat::RGBA8, 4, 4, 16 },
|
||||
{ EGXTexelFormat::RGBA8, ETexelFormat::RGBA8, 4, 4, 32 },
|
||||
{ EGXTexelFormat::CMPR, ETexelFormat::RGBA8, 8, 8, 4 },
|
||||
};
|
||||
|
||||
/** Remap an ETexelFormat to the closest EGXTexelFormat */
|
||||
const EGXTexelFormat kEditorFormatToGameFormat[] =
|
||||
{
|
||||
// Luminance
|
||||
EGXTexelFormat::I8,
|
||||
// LuminanceAlpha
|
||||
EGXTexelFormat::IA8,
|
||||
// RGB565
|
||||
EGXTexelFormat::RGB565,
|
||||
// RGBA8
|
||||
EGXTexelFormat::RGBA8,
|
||||
};
|
||||
|
||||
/** Retrieve the format info for a given texel format */
|
||||
const STexelFormatInfo& GetTexelFormatInfo(EGXTexelFormat Format)
|
||||
{
|
||||
uint FormatIdx = (uint) Format;
|
||||
ASSERT( FormatIdx >= 0 && FormatIdx < ARRAY_SIZE(kTexelFormatInfo) );
|
||||
|
||||
return kTexelFormatInfo[(uint) Format];
|
||||
}
|
||||
|
||||
const STexelFormatInfo& GetTexelFormatInfo(ETexelFormat Format)
|
||||
{
|
||||
return GetTexelFormatInfo( kEditorFormatToGameFormat[(uint) Format] );
|
||||
}
|
||||
|
||||
/** Utility functions useful for converting image formats */
|
||||
FORCEINLINE static uint8 Extend3to8(uint8 In)
|
||||
{
|
||||
In &= 0x7;
|
||||
return (In << 5) | (In << 2) | (In >> 1);
|
||||
}
|
||||
|
||||
FORCEINLINE static uint8 Extend4to8(uint8 In)
|
||||
{
|
||||
In &= 0xF;
|
||||
return (In << 4) | In;
|
||||
}
|
||||
|
||||
FORCEINLINE static uint8 Extend5to8(uint8 In)
|
||||
{
|
||||
In &= 0x1F;
|
||||
return (In << 3) | (In >> 2);
|
||||
}
|
||||
|
||||
FORCEINLINE static uint8 Extend6to8(uint8 In)
|
||||
{
|
||||
In &= 0x3F;
|
||||
return (In << 2) | (In >> 4);
|
||||
}
|
||||
|
||||
/** Decompose an RGBA dword into 8-bit components */
|
||||
FORCEINLINE static void DecomposeRGBA8(uint32 In,
|
||||
uint8& OutR,
|
||||
uint8& OutG,
|
||||
uint8& OutB,
|
||||
uint8& OutA)
|
||||
{
|
||||
OutR = (In >> 24) & 0xFF;
|
||||
OutG = (In >> 16) & 0xFF;
|
||||
OutB = (In >> 8) & 0xFF;
|
||||
OutA = (In >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
/** Compose an RGBA dword from 8-bit components */
|
||||
FORCEINLINE static uint32 ComposeRGBA8(uint8 R,
|
||||
uint8 G,
|
||||
uint8 B,
|
||||
uint8 A)
|
||||
{
|
||||
return (R << 24) | (G << 16) | (B << 8) | A;
|
||||
}
|
||||
|
||||
/** Convert an RGB5A3 word into an RGBA8 dword */
|
||||
static uint32 RGB5A3toRGBA8(uint16 Texel)
|
||||
{
|
||||
// RGB5A3 uses a per-texel sign bit to swap between two formats.
|
||||
// Based on this bit, the texel is either RGB555 or RGB4A3.
|
||||
uint8 R, G, B, A;
|
||||
|
||||
if (Texel & 0x8000)
|
||||
{
|
||||
R = Extend5to8(Texel >> 10);
|
||||
G = Extend5to8(Texel >> 5);
|
||||
B = Extend5to8(Texel >> 0);
|
||||
A = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
R = Extend4to8(Texel >> 11);
|
||||
G = Extend4to8(Texel >> 7);
|
||||
B = Extend4to8(Texel >> 3);
|
||||
A = Extend3to8(Texel >> 0);
|
||||
}
|
||||
|
||||
return ComposeRGBA8(R, G, B, A);
|
||||
}
|
||||
|
||||
/** Convert an RGB565 word into an RGBA8 dword */
|
||||
static uint32 RGB565toRGBA8(uint16 Texel)
|
||||
{
|
||||
uint8 R = Extend5to8( (Texel >> 11) & 0x1F );
|
||||
uint8 G = Extend6to8( (Texel >> 5) & 0x3F );
|
||||
uint8 B = Extend5to8( (Texel >> 0) & 0x1F );
|
||||
return ComposeRGBA8(R, G, B, 255);
|
||||
}
|
||||
|
||||
/** A texel block in the CMPR/BC1 format */
|
||||
struct SCMPRBlock
|
||||
{
|
||||
uint16 C0;
|
||||
uint16 C1;
|
||||
uint8 Idx[4];
|
||||
};
|
||||
|
||||
/** Extract the four palette colors from a CMPR texel block */
|
||||
static void DecomposeCMPRPalettes(const SCMPRBlock& kBlock,
|
||||
uint32& OutC0,
|
||||
uint32& OutC1,
|
||||
uint32& OutC2,
|
||||
uint32& OutC3)
|
||||
{
|
||||
// Get block palette colors and decompose into RGBA components
|
||||
uint8 R0, G0, B0, A0,
|
||||
R1, G1, B1, A1,
|
||||
R2, G2, B2, A2,
|
||||
R3, G3, B3, A3;
|
||||
|
||||
OutC0 = RGB565toRGBA8(kBlock.C0);
|
||||
OutC1 = RGB565toRGBA8(kBlock.C1);
|
||||
DecomposeRGBA8(OutC0, R0, G0, B0, A0);
|
||||
DecomposeRGBA8(OutC1, R1, G1, B1, A1);
|
||||
|
||||
// Interpolate to get the remaining palette colors
|
||||
if (kBlock.C0 > kBlock.C1)
|
||||
{
|
||||
// GameCube hardware interpolates at 3/8 and 5/8 points
|
||||
// This differs from PC DXT1 implementation that interpolates at 1/3 and 2/3
|
||||
R2 = (R0*5 + R1*3) >> 3;
|
||||
G2 = (G0*5 + G1*3) >> 3;
|
||||
B2 = (B0*5 + B1*3) >> 3;
|
||||
A2 = 255;
|
||||
|
||||
R3 = (R0*3 + R1*5) >> 3;
|
||||
G3 = (G0*3 + G1*5) >> 3;
|
||||
B3 = (B0*3 + B1*5) >> 3;
|
||||
A3 = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
// GameCube hardware sets the color of C3 as the same as C2 instead of black
|
||||
R2 = R3 = (R0 + R1) / 2;
|
||||
G2 = G3 = (G0 + G1) / 2;
|
||||
B2 = B3 = (B0 + B1) / 2;
|
||||
A2 = 255, A3 = 0;
|
||||
}
|
||||
|
||||
OutC2 = ComposeRGBA8(R2, G2, B2, A2);
|
||||
OutC3 = ComposeRGBA8(R3, G3, B3, A3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts game texel data to the corresponding editor format.
|
||||
* "Game Data" is essentially the texture data that is contained
|
||||
* in a TXTR file, except without swizzling and with fixed endianness.
|
||||
* The output texel format is the same as specified by GetTexelFormatInfo().
|
||||
*/
|
||||
void ConvertGameDataToEditorData(EGXTexelFormat SrcFormat,
|
||||
uint32 SizeX,
|
||||
uint32 SizeY,
|
||||
const uint8* pkSrcData,
|
||||
uint SrcDataSize,
|
||||
std::vector<uint8>& DstData,
|
||||
ETexelFormat* pOutTexelFormat /*= nullptr*/)
|
||||
{
|
||||
//@todo palette formats are unsupported
|
||||
const STexelFormatInfo& kSrcTexelFormat = GetTexelFormatInfo( SrcFormat );
|
||||
const STexelFormatInfo& kDstTexelFormat = GetTexelFormatInfo( kSrcTexelFormat.EditorTexelFormat );
|
||||
uint SrcSize = (SizeX * SizeY * kSrcTexelFormat.BitsPerPixel) / 8;
|
||||
uint DstSize = (SizeX * SizeY * kDstTexelFormat.BitsPerPixel) / 8;
|
||||
ASSERT( SrcDataSize >= SrcSize );
|
||||
|
||||
DstData.resize( DstSize );
|
||||
memset(DstData.data(), 0xFF, DstData.size());
|
||||
uint8* pDstData = DstData.data();
|
||||
|
||||
if (pOutTexelFormat)
|
||||
{
|
||||
*pOutTexelFormat = kDstTexelFormat.EditorTexelFormat;
|
||||
}
|
||||
|
||||
// Some game formats are the same as the editor format.
|
||||
// In these cases we can simply copy the buffer over.
|
||||
if( SrcFormat == EGXTexelFormat::I8 ||
|
||||
SrcFormat == EGXTexelFormat::IA8 ||
|
||||
SrcFormat == EGXTexelFormat::RGB565 ||
|
||||
SrcFormat == EGXTexelFormat::RGBA8 )
|
||||
{
|
||||
memcpy( pDstData, pkSrcData, DstSize );
|
||||
}
|
||||
|
||||
// CMPR requires special handling. There are small differences between CMPR and DXT1
|
||||
// that prevents us from using hardware DXT1 support, so it must be decoded to RGBA8.
|
||||
else if( SrcFormat == EGXTexelFormat::CMPR )
|
||||
{
|
||||
const SCMPRBlock* pkBlock = (const SCMPRBlock*) pkSrcData;
|
||||
uint32* pDst32 = (uint32*) pDstData;
|
||||
|
||||
for (uint Y = 0; Y < SizeY; Y += 4)
|
||||
{
|
||||
for (uint X = 0; X < SizeX; X += 4)
|
||||
{
|
||||
uint32 Palettes[4];
|
||||
DecomposeCMPRPalettes(*pkBlock, Palettes[0], Palettes[1],
|
||||
Palettes[2], Palettes[3] );
|
||||
|
||||
// Byte-swap the palettes so they will be written in RGBA order
|
||||
if (EEndian::SystemEndian == EEndian::LittleEndian)
|
||||
{
|
||||
SwapBytes(Palettes[0]);
|
||||
SwapBytes(Palettes[1]);
|
||||
SwapBytes(Palettes[2]);
|
||||
SwapBytes(Palettes[3]);
|
||||
}
|
||||
|
||||
for (uint DY = 0; DY < 4; DY++)
|
||||
{
|
||||
uint8 Bits = pkBlock->Idx[DY];
|
||||
|
||||
for (uint DX = 0; DX < 4; DX++)
|
||||
{
|
||||
uint Shift = DX*2;
|
||||
uint Index = (Bits >> Shift) & 0x3;
|
||||
uint DstOffset = (Y+DY)*SizeX + X+DX;
|
||||
pDst32[DstOffset] = Palettes[Index];
|
||||
}
|
||||
}
|
||||
|
||||
pkBlock++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining formats require conversion because they aren't supported by PC graphics cards.
|
||||
else
|
||||
{
|
||||
// Sweep texels left to right, top to bottom, and apply conversions.
|
||||
for (uint Y = 0; Y < SizeY; Y++)
|
||||
{
|
||||
for (uint X = 0; X < SizeX; X++)
|
||||
{
|
||||
switch ( SrcFormat )
|
||||
{
|
||||
// I4/IA4: Extend greyscale and alpha (for IA4) to 8-bit.
|
||||
// Note I4 has two 1-component texels, whereas IA4 has one 2-component texels.
|
||||
// But this data is converted to the output data the same way either way
|
||||
case EGXTexelFormat::I4:
|
||||
case EGXTexelFormat::IA4:
|
||||
{
|
||||
uint8 I = *pkSrcData++;
|
||||
*pDstData++ = Extend3to8( (I >> 4) & 0xF );
|
||||
*pDstData++ = Extend3to8( (I >> 0) & 0xF );
|
||||
break;
|
||||
}
|
||||
|
||||
// RGB5A3: Convert to raw RGBA8.
|
||||
case EGXTexelFormat::RGB5A3:
|
||||
{
|
||||
uint8 V0 = *pkSrcData++;
|
||||
uint8 V1 = *pkSrcData++;
|
||||
uint32 RGBA = RGB5A3toRGBA8( (V1 << 8) | V0 );
|
||||
SwapBytes(RGBA);
|
||||
|
||||
*((uint32*) pDstData) = RGBA;
|
||||
pDstData += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled
|
||||
errorf("Unhandled texel format in ConvertGameDataToEditorData(): %s",
|
||||
TEnumReflection<EGXTexelFormat>::ConvertValueToString(SrcFormat) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Increment I4 again to account for the fact that we read two texels instead of one
|
||||
if( SrcFormat == EGXTexelFormat::I4 )
|
||||
{
|
||||
X++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Decode editor texel data to RGBA texels */
|
||||
void ConvertEditorDataToRGBA(ETexelFormat SrcFormat,
|
||||
uint32 SizeX,
|
||||
uint32 SizeY,
|
||||
const uint8* pkSrcData,
|
||||
uint SrcDataSize,
|
||||
std::vector<uint8>& DstData)
|
||||
{
|
||||
const STexelFormatInfo& kFormatInfo = GetTexelFormatInfo(SrcFormat);
|
||||
uint32 SrcSize = (SizeX * SizeY * kFormatInfo.BitsPerPixel) / 8;
|
||||
uint32 DstSize = (SizeX * SizeY);
|
||||
ASSERT( SrcDataSize >= SrcSize);
|
||||
DstData.resize(DstSize);
|
||||
uint8* pDstData = DstData.data();
|
||||
|
||||
// If data is already RGBA, then no conversion is needed.
|
||||
if (SrcFormat == ETexelFormat::RGBA8)
|
||||
{
|
||||
memcpy( pDstData, pkSrcData, DstSize );
|
||||
return;
|
||||
}
|
||||
|
||||
// Other formats require conversion
|
||||
for (uint Y = 0; Y < SizeY; Y++)
|
||||
{
|
||||
for (uint X = 0; X < SizeX; X++)
|
||||
{
|
||||
uint8 R, G, B, A;
|
||||
|
||||
switch (SrcFormat)
|
||||
{
|
||||
|
||||
// Luminance: Replicate to all channels and set opaque alpha
|
||||
case ETexelFormat::Luminance:
|
||||
{
|
||||
R = G = B = *pkSrcData++;
|
||||
A = 255;
|
||||
break;
|
||||
}
|
||||
|
||||
// LuminanceAlpha: Replicate greyscale to all channels and preserve alpha
|
||||
case ETexelFormat::LuminanceAlpha:
|
||||
{
|
||||
R = G = B = *pkSrcData++;
|
||||
A = *pkSrcData++;
|
||||
break;
|
||||
}
|
||||
|
||||
// RGB565: Extend RGB components to 8-bit and set opaque alpha
|
||||
case ETexelFormat::RGB565:
|
||||
{
|
||||
uint8 Byte0 = *pkSrcData++;
|
||||
uint8 Byte1 = *pkSrcData++;
|
||||
uint16 Texel = (Byte1 << 8) | Byte0;
|
||||
uint32 Decoded = RGB565toRGBA8(Texel);
|
||||
DecomposeRGBA8(Decoded, R, G, B, A);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*pDstData++ = R;
|
||||
*pDstData++ = G;
|
||||
*pDstData++ = B;
|
||||
*pDstData++ = A;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Encode RGBA texels into a game compression format */
|
||||
void CompressRGBA(uint32 SizeX,
|
||||
uint32 SizeY,
|
||||
const uint8* pkSrcData,
|
||||
uint SrcDataSize,
|
||||
EGXTexelFormat DstFormat,
|
||||
std::vector<uint8>& DstData)
|
||||
{
|
||||
//@todo
|
||||
}
|
||||
|
||||
/** Import the image file at the given path. Returns true if succeeded. */
|
||||
bool LoadImageFromFile(const TString& kPath,
|
||||
std::vector<uint8>& OutBuffer,
|
||||
int& OutSizeX,
|
||||
int& OutSizeY,
|
||||
int& OutNumChannels,
|
||||
int DesiredNumChannels /*= 4*/)
|
||||
{
|
||||
std::vector<uint8> DataBuffer;
|
||||
FileUtil::LoadFileToBuffer(kPath, DataBuffer);
|
||||
return LoadImageFromMemory(DataBuffer.data(), DataBuffer.size(), OutBuffer, OutSizeX, OutSizeY, OutNumChannels, DesiredNumChannels);
|
||||
}
|
||||
|
||||
/** Import an image file from a buffer. Returns true if succeeded. */
|
||||
bool LoadImageFromMemory(void* pData,
|
||||
uint DataSize,
|
||||
std::vector<uint8>& OutBuffer,
|
||||
int& OutSizeX,
|
||||
int& OutSizeY,
|
||||
int& OutNumChannels,
|
||||
int DesiredNumChannels /*= 4*/)
|
||||
{
|
||||
stbi_uc* pOutData = stbi_load_from_memory( (const stbi_uc*) pData, DataSize, &OutSizeX, &OutSizeY, &OutNumChannels, DesiredNumChannels );
|
||||
|
||||
if (pOutData)
|
||||
{
|
||||
uint32 NumChannels = (DesiredNumChannels > 0 ? DesiredNumChannels : OutNumChannels);
|
||||
uint32 ImageSize = (OutSizeX * OutSizeY * NumChannels);
|
||||
OutBuffer.resize(ImageSize);
|
||||
memcpy(OutBuffer.data(), pOutData, ImageSize);
|
||||
|
||||
STBI_FREE(pOutData);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace NImageUtils
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef NTEXTUREUTILS_H
|
||||
#define NTEXTUREUTILS_H
|
||||
|
||||
#include "ETexelFormat.h"
|
||||
#include <Common/BasicTypes.h>
|
||||
#include <Common/TString.h>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Various utility functions for working with textures and images.
|
||||
* For import functions, the following formats are supported:
|
||||
* JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
|
||||
**/
|
||||
namespace NTextureUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* Retrieve the format info for a given texel format.
|
||||
* Note: EditorTexelFormat field is invalid for palette formats (C4, C8, C14x2).
|
||||
* For these, fetch the format info of the underlying texel format.
|
||||
*/
|
||||
const STexelFormatInfo& GetTexelFormatInfo(EGXTexelFormat Format);
|
||||
const STexelFormatInfo& GetTexelFormatInfo(ETexelFormat Format);
|
||||
|
||||
/**
|
||||
* Converts game texel data to the corresponding editor format.
|
||||
* "Game Data" is essentially the texture data that is contained
|
||||
* in a TXTR file, except without swizzling and with fixed endianness.
|
||||
* The output texel format is the same as specified by GetTexelFormatInfo().
|
||||
*/
|
||||
void ConvertGameDataToEditorData(EGXTexelFormat SrcFormat,
|
||||
uint32 SizeX,
|
||||
uint32 SizeY,
|
||||
const uint8* pkSrcData,
|
||||
uint SrcDataSize,
|
||||
std::vector<uint8>& DstData,
|
||||
ETexelFormat* pOutTexelFormat = nullptr);
|
||||
|
||||
/** Decode editor texel data to RGBA texels */
|
||||
void ConvertEditorDataToRGBA(ETexelFormat SrcFormat,
|
||||
uint32 SizeX,
|
||||
uint32 SizeY,
|
||||
const uint8* pkSrcData,
|
||||
uint SrcDataSize,
|
||||
std::vector<uint8>& DstData);
|
||||
|
||||
/** Encode RGBA texels into a game compression format */
|
||||
void CompressRGBA(uint32 SizeX,
|
||||
uint32 SizeY,
|
||||
const uint8* pkSrcData,
|
||||
uint SrcDataSize,
|
||||
EGXTexelFormat DstFormat,
|
||||
std::vector<uint8>& DstData);
|
||||
|
||||
/** Import the image file at the given path. Returns true if succeeded. */
|
||||
bool LoadImageFromFile(const TString& kPath,
|
||||
std::vector<uint8>& OutBuffer,
|
||||
int& OutSizeX,
|
||||
int& OutSizeY,
|
||||
int& OutNumChannels,
|
||||
int DesiredNumChannels = 0);
|
||||
|
||||
/** Import an image file from a buffer. Returns true if succeeded. */
|
||||
bool LoadImageFromMemory(void* pData,
|
||||
uint DataSize,
|
||||
std::vector<uint8>& OutBuffer,
|
||||
int& OutSizeX,
|
||||
int& OutSizeY,
|
||||
int& OutNumChannels,
|
||||
int DesiredNumChannels = 0);
|
||||
|
||||
}
|
||||
|
||||
#endif // NTEXTUREUTILS_H
|
|
@ -91,10 +91,7 @@ CModelEditorWindow::CModelEditorWindow(CModel *pModel, QWidget *pParent)
|
|||
ui->AnimParamCSpinBox->setProperty ("ModelEditorWidgetType", (int) EModelEditorWidget::AnimParamCSpinBox);
|
||||
ui->AnimParamDSpinBox->setProperty ("ModelEditorWidgetType", (int) EModelEditorWidget::AnimParamDSpinBox);
|
||||
|
||||
connect(ui->ActionImport, SIGNAL(triggered()), this, SLOT(Import()));
|
||||
connect(ui->ActionSave, SIGNAL(triggered()), this, SLOT(Save()));
|
||||
connect(ui->ActionConvertToDDS, SIGNAL(triggered()), this, SLOT(ConvertToDDS()));
|
||||
connect(ui->ActionConvertToTXTR, SIGNAL(triggered()), this, SLOT(ConvertToTXTR()));
|
||||
connect(ui->MeshPreviewButton, SIGNAL(clicked()), this, SLOT(SetMeshPreview()));
|
||||
connect(ui->SpherePreviewButton, SIGNAL(clicked()), this, SLOT(SetSpherePreview()));
|
||||
connect(ui->FlatPreviewButton, SIGNAL(clicked()), this, SLOT(SetFlatPreview()));
|
||||
|
@ -765,55 +762,6 @@ void CModelEditorWindow::Import()
|
|||
gpResourceStore->DestroyUnreferencedResources();
|
||||
}
|
||||
|
||||
void CModelEditorWindow::ConvertToDDS()
|
||||
{
|
||||
QString Input = QFileDialog::getOpenFileName(this, "Retro Texture (*.TXTR)", "", "*.TXTR");
|
||||
if (Input.isEmpty()) return;
|
||||
|
||||
TString TexFilename = TO_TSTRING(Input);
|
||||
CFileInStream InTextureFile(TexFilename, EEndian::LittleEndian);
|
||||
CTexture *pTex = CTextureDecoder::LoadTXTR( InTextureFile, nullptr );
|
||||
|
||||
TString OutName = TexFilename.GetFilePathWithoutExtension() + ".dds";
|
||||
CFileOutStream Out(OutName, EEndian::LittleEndian);
|
||||
if (!Out.IsValid()) QMessageBox::warning(this, "Error", "Couldn't open output DDS!");
|
||||
|
||||
else
|
||||
{
|
||||
bool Success = pTex->WriteDDS(Out);
|
||||
if (!Success) QMessageBox::warning(this, "Error", "Couldn't write output DDS!");
|
||||
else QMessageBox::information(this, "Success", "Successfully converted to DDS!");
|
||||
}
|
||||
|
||||
delete pTex;
|
||||
}
|
||||
|
||||
void CModelEditorWindow::ConvertToTXTR()
|
||||
{
|
||||
QString Input = QFileDialog::getOpenFileName(this, "DirectDraw Surface (*.dds)", "", "*.dds");
|
||||
if (Input.isEmpty()) return;
|
||||
|
||||
TString TexFilename = TO_TSTRING(Input);
|
||||
CFileInStream InTextureFile = CFileInStream(TexFilename, EEndian::LittleEndian);
|
||||
CTexture *pTex = CTextureDecoder::LoadDDS(InTextureFile, nullptr);
|
||||
TString OutName = TexFilename.GetFilePathWithoutExtension() + ".txtr";
|
||||
|
||||
if ((pTex->TexelFormat() != ETexelFormat::DXT1) || (pTex->NumMipMaps() > 1))
|
||||
QMessageBox::warning(this, "Error", "Can't convert DDS to TXTR! Save your texture as a DXT1 DDS with no mipmaps, then try again.");
|
||||
|
||||
else
|
||||
{
|
||||
CFileOutStream Out(OutName, EEndian::BigEndian);
|
||||
if (!Out.IsValid()) QMessageBox::warning(this, "Error", "Couldn't open output TXTR!");
|
||||
|
||||
else
|
||||
{
|
||||
CTextureEncoder::EncodeTXTR(Out, pTex, ETexelFormat::GX_CMPR);
|
||||
QMessageBox::information(this, "Success", "Successfully converted to TXTR!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CModelEditorWindow::SetMeshPreview()
|
||||
{
|
||||
ui->Viewport->SetDrawMode(CModelEditorViewport::EDrawMode::DrawMesh);
|
||||
|
|
|
@ -101,8 +101,6 @@ private:
|
|||
|
||||
private slots:
|
||||
void Import();
|
||||
void ConvertToDDS();
|
||||
void ConvertToTXTR();
|
||||
void SetMeshPreview();
|
||||
void SetSpherePreview();
|
||||
void SetFlatPreview();
|
||||
|
|
|
@ -172,9 +172,9 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>-489</y>
|
||||
<width>308</width>
|
||||
<height>1184</height>
|
||||
<y>0</y>
|
||||
<width>319</width>
|
||||
<height>1302</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
|
@ -2407,7 +2407,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1280</width>
|
||||
<height>21</height>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
|
@ -2415,17 +2415,8 @@
|
|||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="ActionSave"/>
|
||||
<addaction name="ActionImport"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTextures">
|
||||
<property name="title">
|
||||
<string>Textures</string>
|
||||
</property>
|
||||
<addaction name="ActionConvertToDDS"/>
|
||||
<addaction name="ActionConvertToTXTR"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuTextures"/>
|
||||
</widget>
|
||||
<action name="ActionSave">
|
||||
<property name="icon">
|
||||
|
|
Loading…
Reference in New Issue