Rebase TextureImporting onto cmake branch
This commit is contained in:
parent
b507196851
commit
d969c0d053
|
@ -1,3 +1,6 @@
|
||||||
[submodule "externals/LibCommon"]
|
[submodule "externals/LibCommon"]
|
||||||
path = externals/LibCommon
|
path = externals/LibCommon
|
||||||
url = ../LibCommon.git
|
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)
|
set(CODEGEN_BUILD_PACKAGE_DURING_CONFIGURE ON)
|
||||||
add_subdirectory(externals/LibCommon)
|
add_subdirectory(externals/LibCommon)
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/externals/stb)
|
||||||
|
|
||||||
add_subdirectory(src/Core)
|
add_subdirectory(src/Core)
|
||||||
add_subdirectory(src/Editor)
|
add_subdirectory(src/Editor)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 1034f5e5c4809ea0a7f4387e0cd37c5184de3cdd
|
|
@ -4,7 +4,7 @@
|
||||||
#include "CResourceStore.h"
|
#include "CResourceStore.h"
|
||||||
#include "CVirtualDirectory.h"
|
#include "CVirtualDirectory.h"
|
||||||
#include "Core/Resource/CResTypeInfo.h"
|
#include "Core/Resource/CResTypeInfo.h"
|
||||||
#include "Core/Resource/EResType.h"
|
#include "Core/Resource/EResourceType.h"
|
||||||
#include <Common/CAssetID.h>
|
#include <Common/CAssetID.h>
|
||||||
#include <Common/CFourCC.h>
|
#include <Common/CFourCC.h>
|
||||||
#include <Common/Flags.h>
|
#include <Common/Flags.h>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define CRESOURCESTORE_H
|
#define CRESOURCESTORE_H
|
||||||
|
|
||||||
#include "CVirtualDirectory.h"
|
#include "CVirtualDirectory.h"
|
||||||
#include "Core/Resource/EResType.h"
|
#include "Core/Resource/EResourceType.h"
|
||||||
#include <Common/CAssetID.h>
|
#include <Common/CAssetID.h>
|
||||||
#include <Common/CFourCC.h>
|
#include <Common/CFourCC.h>
|
||||||
#include <Common/FileUtil.h>
|
#include <Common/FileUtil.h>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define CVIRTUALDIRECTORY
|
#define CVIRTUALDIRECTORY
|
||||||
|
|
||||||
/* Virtual directory system used to look up resources by their location in the filesystem. */
|
/* 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/Macros.h>
|
||||||
#include <Common/TString.h>
|
#include <Common/TString.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef NCORETESTS_H
|
#ifndef NCORETESTS_H
|
||||||
#define NCORETESTS_H
|
#define NCORETESTS_H
|
||||||
|
|
||||||
#include "Core/Resource/EResType.h"
|
#include "Core/Resource/EResourceType.h"
|
||||||
|
|
||||||
/** Unit tests for Core */
|
/** Unit tests for Core */
|
||||||
namespace NCoreTests
|
namespace NCoreTests
|
||||||
|
|
|
@ -46,9 +46,10 @@ void CFramebuffer::Init()
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
|
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
|
||||||
|
|
||||||
mpRenderbuffer = new CRenderbuffer(mWidth, mHeight);
|
mpRenderbuffer = new CRenderbuffer(mWidth, mHeight);
|
||||||
mpTexture = new CTexture(mWidth, mHeight);
|
|
||||||
mpRenderbuffer->SetMultisamplingEnabled(mEnableMultisampling);
|
mpRenderbuffer->SetMultisamplingEnabled(mEnableMultisampling);
|
||||||
|
mpTexture = new CTexture(mWidth, mHeight);
|
||||||
mpTexture->SetMultisamplingEnabled(mEnableMultisampling);
|
mpTexture->SetMultisamplingEnabled(mEnableMultisampling);
|
||||||
|
mpTexture->CreateRenderResources();
|
||||||
InitBuffers();
|
InitBuffers();
|
||||||
mInitialized = true;
|
mInitialized = true;
|
||||||
}
|
}
|
||||||
|
@ -101,9 +102,9 @@ void CFramebuffer::InitBuffers()
|
||||||
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mpRenderbuffer->BufferID()
|
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mpRenderbuffer->BufferID()
|
||||||
);
|
);
|
||||||
|
|
||||||
mpTexture->Bind(0);
|
mpTexture->BindToSampler(0);
|
||||||
glFramebufferTexture2D(
|
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);
|
mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define CFRAMEBUFFER_H
|
#define CFRAMEBUFFER_H
|
||||||
|
|
||||||
#include "CRenderbuffer.h"
|
#include "CRenderbuffer.h"
|
||||||
#include "Core/Resource/CTexture.h"
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
|
|
||||||
class CFramebuffer
|
class CFramebuffer
|
||||||
|
|
|
@ -237,7 +237,7 @@ void CDrawUtil::DrawBillboard(CTexture* pTexture, const CVector3f& Position, con
|
||||||
static GLuint TintLoc = mpBillboardShader->GetUniformLocation("TintColor");
|
static GLuint TintLoc = mpBillboardShader->GetUniformLocation("TintColor");
|
||||||
glUniform4f(TintLoc, Tint.R, Tint.G, Tint.B, Tint.A);
|
glUniform4f(TintLoc, Tint.R, Tint.G, Tint.B, Tint.A);
|
||||||
|
|
||||||
pTexture->Bind(0);
|
pTexture->BindToSampler(0);
|
||||||
|
|
||||||
// Set other properties
|
// Set other properties
|
||||||
CMaterial::KillCachedMaterial();
|
CMaterial::KillCachedMaterial();
|
||||||
|
@ -271,8 +271,8 @@ void CDrawUtil::DrawLightBillboard(ELightType Type, const CColor& LightColor, co
|
||||||
|
|
||||||
CTexture *pTexA = GetLightTexture(Type);
|
CTexture *pTexA = GetLightTexture(Type);
|
||||||
CTexture *pTexB = GetLightMask(Type);
|
CTexture *pTexB = GetLightMask(Type);
|
||||||
pTexA->Bind(0);
|
pTexA->BindToSampler(0);
|
||||||
pTexB->Bind(1);
|
pTexB->BindToSampler(1);
|
||||||
|
|
||||||
static GLuint TextureLoc = mpLightBillboardShader->GetUniformLocation("Texture");
|
static GLuint TextureLoc = mpLightBillboardShader->GetUniformLocation("Texture");
|
||||||
static GLuint MaskLoc = mpLightBillboardShader->GetUniformLocation("LightMask");
|
static GLuint MaskLoc = mpLightBillboardShader->GetUniformLocation("LightMask");
|
||||||
|
@ -360,7 +360,7 @@ CShader* CDrawUtil::GetTextShader()
|
||||||
void CDrawUtil::LoadCheckerboardTexture(uint32 GLTextureUnit)
|
void CDrawUtil::LoadCheckerboardTexture(uint32 GLTextureUnit)
|
||||||
{
|
{
|
||||||
Init();
|
Init();
|
||||||
mpCheckerTexture->Bind(GLTextureUnit);
|
mpCheckerTexture->BindToSampler(GLTextureUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTexture* CDrawUtil::GetLightTexture(ELightType Type)
|
CTexture* CDrawUtil::GetLightTexture(ELightType Type)
|
||||||
|
|
|
@ -188,7 +188,7 @@ void CRenderer::RenderBloom()
|
||||||
|
|
||||||
CDrawUtil::UseTextureShader();
|
CDrawUtil::UseTextureShader();
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
|
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
|
||||||
mPostProcessFramebuffer.Texture()->Bind(0);
|
mPostProcessFramebuffer.Texture()->BindToSampler(0);
|
||||||
CDrawUtil::DrawSquare();
|
CDrawUtil::DrawSquare();
|
||||||
|
|
||||||
// Pass 2: Horizontal blur
|
// Pass 2: Horizontal blur
|
||||||
|
@ -197,7 +197,7 @@ void CRenderer::RenderBloom()
|
||||||
|
|
||||||
CDrawUtil::UseTextureShader(CColor::skGray);
|
CDrawUtil::UseTextureShader(CColor::skGray);
|
||||||
glBlendFunc(GL_ONE, GL_ZERO);
|
glBlendFunc(GL_ONE, GL_ZERO);
|
||||||
mBloomFramebuffers[0].Texture()->Bind(0);
|
mBloomFramebuffers[0].Texture()->BindToSampler(0);
|
||||||
CDrawUtil::DrawSquare();
|
CDrawUtil::DrawSquare();
|
||||||
|
|
||||||
for (uint32 iPass = 0; iPass < 6; iPass++)
|
for (uint32 iPass = 0; iPass < 6; iPass++)
|
||||||
|
@ -217,7 +217,7 @@ void CRenderer::RenderBloom()
|
||||||
|
|
||||||
CDrawUtil::UseTextureShader(CColor::skGray);
|
CDrawUtil::UseTextureShader(CColor::skGray);
|
||||||
glBlendFunc(GL_ONE, GL_ZERO);
|
glBlendFunc(GL_ONE, GL_ZERO);
|
||||||
mBloomFramebuffers[1].Texture()->Bind(0);
|
mBloomFramebuffers[1].Texture()->BindToSampler(0);
|
||||||
CDrawUtil::DrawSquare();
|
CDrawUtil::DrawSquare();
|
||||||
|
|
||||||
for (uint32 iPass = 0; iPass < 6; iPass++)
|
for (uint32 iPass = 0; iPass < 6; iPass++)
|
||||||
|
@ -240,7 +240,7 @@ void CRenderer::RenderBloom()
|
||||||
|
|
||||||
CDrawUtil::UseTextureShader();
|
CDrawUtil::UseTextureShader();
|
||||||
glBlendFunc(GL_ONE, GL_ONE);
|
glBlendFunc(GL_ONE, GL_ONE);
|
||||||
mBloomFramebuffers[2].Texture()->Bind(0);
|
mBloomFramebuffers[2].Texture()->BindToSampler(0);
|
||||||
CDrawUtil::DrawSquare();
|
CDrawUtil::DrawSquare();
|
||||||
|
|
||||||
if (mBloomMode == EBloomMode::BloomMaps)
|
if (mBloomMode == EBloomMode::BloomMaps)
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "Core/OpenGL/CFramebuffer.h"
|
#include "Core/OpenGL/CFramebuffer.h"
|
||||||
#include "Core/Resource/CFont.h"
|
#include "Core/Resource/CFont.h"
|
||||||
#include "Core/Resource/CLight.h"
|
#include "Core/Resource/CLight.h"
|
||||||
#include "Core/Resource/CTexture.h"
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
#include "Core/Scene/CSceneNode.h"
|
#include "Core/Scene/CSceneNode.h"
|
||||||
|
|
||||||
#include <Common/CColor.h>
|
#include <Common/CColor.h>
|
||||||
|
|
|
@ -42,7 +42,7 @@ CVector2f CFont::RenderString(const TString& rkString, CRenderer* /*pRenderer*/,
|
||||||
GLuint ModelMtxLoc = pTextShader->GetUniformLocation("ModelMtx");
|
GLuint ModelMtxLoc = pTextShader->GetUniformLocation("ModelMtx");
|
||||||
GLuint ColorLoc = pTextShader->GetUniformLocation("FontColor");
|
GLuint ColorLoc = pTextShader->GetUniformLocation("FontColor");
|
||||||
GLuint LayerLoc = pTextShader->GetUniformLocation("RGBALayer");
|
GLuint LayerLoc = pTextShader->GetUniformLocation("RGBALayer");
|
||||||
mpFontTexture->Bind(0);
|
mpFontTexture->BindToSampler(0);
|
||||||
smGlyphVertices->Bind();
|
smGlyphVertices->Bind();
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define CFONT_H
|
#define CFONT_H
|
||||||
|
|
||||||
#include "CResource.h"
|
#include "CResource.h"
|
||||||
#include "CTexture.h"
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
#include "TResPtr.h"
|
#include "TResPtr.h"
|
||||||
#include "Core/Resource/Model/CVertex.h"
|
#include "Core/Resource/Model/CVertex.h"
|
||||||
#include "Core/OpenGL/CDynamicVertexBuffer.h"
|
#include "Core/OpenGL/CDynamicVertexBuffer.h"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define MATERIAL_H
|
#define MATERIAL_H
|
||||||
|
|
||||||
#include "CMaterialPass.h"
|
#include "CMaterialPass.h"
|
||||||
#include "CTexture.h"
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
#include "TResPtr.h"
|
#include "TResPtr.h"
|
||||||
#include "Core/Resource/Model/EVertexAttribute.h"
|
#include "Core/Resource/Model/EVertexAttribute.h"
|
||||||
#include "Core/Render/FRenderOptions.h"
|
#include "Core/Render/FRenderOptions.h"
|
||||||
|
|
|
@ -81,7 +81,7 @@ void CMaterialPass::HashParameters(CFNV1A& rHash)
|
||||||
void CMaterialPass::LoadTexture(uint32 PassIndex)
|
void CMaterialPass::LoadTexture(uint32 PassIndex)
|
||||||
{
|
{
|
||||||
if (mpTexture)
|
if (mpTexture)
|
||||||
mpTexture->Bind(PassIndex);
|
mpTexture->BindToSampler(PassIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMaterialPass::SetAnimCurrent(FRenderOptions Options, uint32 PassIndex)
|
void CMaterialPass::SetAnimCurrent(FRenderOptions Options, uint32 PassIndex)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define CMATERIALPASS_H
|
#define CMATERIALPASS_H
|
||||||
|
|
||||||
#include "TResPtr.h"
|
#include "TResPtr.h"
|
||||||
#include "CTexture.h"
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
#include "ETevEnums.h"
|
#include "ETevEnums.h"
|
||||||
#include "Core/Render/FRenderOptions.h"
|
#include "Core/Render/FRenderOptions.h"
|
||||||
#include <Common/CFourCC.h>
|
#include <Common/CFourCC.h>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define CMATERIALSET_H
|
#define CMATERIALSET_H
|
||||||
|
|
||||||
#include "CMaterial.h"
|
#include "CMaterial.h"
|
||||||
#include "CTexture.h"
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
#include <Common/EGame.h>
|
#include <Common/EGame.h>
|
||||||
#include <Common/FileIO/IInputStream.h>
|
#include <Common/FileIO/IInputStream.h>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CRESTYPEFILTER_H
|
#ifndef CRESTYPEFILTER_H
|
||||||
#define CRESTYPEFILTER_H
|
#define CRESTYPEFILTER_H
|
||||||
|
|
||||||
#include "EResType.h"
|
#include "EResourceType.h"
|
||||||
#include "CResTypeInfo.h"
|
#include "CResTypeInfo.h"
|
||||||
#include "Core/GameProject/CResourceEntry.h"
|
#include "Core/GameProject/CResourceEntry.h"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CRESTYPEINFO
|
#ifndef CRESTYPEINFO
|
||||||
#define CRESTYPEINFO
|
#define CRESTYPEINFO
|
||||||
|
|
||||||
#include "EResType.h"
|
#include "EResourceType.h"
|
||||||
#include <Common/CFourCC.h>
|
#include <Common/CFourCC.h>
|
||||||
#include <Common/EGame.h>
|
#include <Common/EGame.h>
|
||||||
#include <Common/Flags.h>
|
#include <Common/Flags.h>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define CRESOURCE_H
|
#define CRESOURCE_H
|
||||||
|
|
||||||
#include "CResTypeInfo.h"
|
#include "CResTypeInfo.h"
|
||||||
#include "EResType.h"
|
#include "EResourceType.h"
|
||||||
#include "Core/GameProject/CDependencyTree.h"
|
#include "Core/GameProject/CDependencyTree.h"
|
||||||
#include "Core/GameProject/CResourceEntry.h"
|
#include "Core/GameProject/CResourceEntry.h"
|
||||||
#include "Core/GameProject/CResourceStore.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 "CTextureEncoder.h"
|
||||||
#include <Common/Log.h>
|
#include <Common/Log.h>
|
||||||
|
|
||||||
|
@ -10,12 +11,12 @@ void CTextureEncoder::WriteTXTR(IOutputStream& rTXTR)
|
||||||
{
|
{
|
||||||
// Only DXT1->CMPR supported at the moment
|
// Only DXT1->CMPR supported at the moment
|
||||||
rTXTR.WriteLong((uint) mOutputFormat);
|
rTXTR.WriteLong((uint) mOutputFormat);
|
||||||
rTXTR.WriteShort(mpTexture->mWidth);
|
rTXTR.WriteShort(mpTexture->SizeX());
|
||||||
rTXTR.WriteShort(mpTexture->mHeight);
|
rTXTR.WriteShort(mpTexture->SizeY());
|
||||||
rTXTR.WriteLong(mpTexture->mNumMipMaps);
|
rTXTR.WriteLong(mpTexture->NumMipMaps());
|
||||||
|
/*
|
||||||
uint32 MipW = mpTexture->Width() / 4;
|
uint32 MipW = mpTexture->SizeX() / 4;
|
||||||
uint32 MipH = mpTexture->Height() / 4;
|
uint32 MipH = mpTexture->SizeY() / 4;
|
||||||
CMemoryInStream Image(mpTexture->mpImgDataBuffer, mpTexture->mImgDataSize, EEndian::LittleEndian);
|
CMemoryInStream Image(mpTexture->mpImgDataBuffer, mpTexture->mImgDataSize, EEndian::LittleEndian);
|
||||||
uint32 MipOffset = Image.Tell();
|
uint32 MipOffset = Image.Tell();
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ void CTextureEncoder::WriteTXTR(IOutputStream& rTXTR)
|
||||||
MipH /= 2;
|
MipH /= 2;
|
||||||
if (MipW < 2) MipW = 2;
|
if (MipW < 2) MipW = 2;
|
||||||
if (MipH < 2) MipH = 2;
|
if (MipH < 2) MipH = 2;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTextureEncoder::DetermineBestOutputFormat()
|
void CTextureEncoder::DetermineBestOutputFormat()
|
||||||
|
@ -61,7 +62,7 @@ void CTextureEncoder::ReadSubBlockCMPR(IInputStream& rSource, IOutputStream& rDe
|
||||||
// ************ STATIC ************
|
// ************ STATIC ************
|
||||||
void CTextureEncoder::EncodeTXTR(IOutputStream& rTXTR, CTexture *pTex)
|
void CTextureEncoder::EncodeTXTR(IOutputStream& rTXTR, CTexture *pTex)
|
||||||
{
|
{
|
||||||
if (pTex->mTexelFormat != ETexelFormat::DXT1)
|
if (pTex->mEditorFormat != ETexelFormat::BC1)
|
||||||
{
|
{
|
||||||
errorf("Unsupported texel format for decoding");
|
errorf("Unsupported texel format for decoding");
|
||||||
return;
|
return;
|
||||||
|
@ -69,7 +70,7 @@ void CTextureEncoder::EncodeTXTR(IOutputStream& rTXTR, CTexture *pTex)
|
||||||
|
|
||||||
CTextureEncoder Encoder;
|
CTextureEncoder Encoder;
|
||||||
Encoder.mpTexture = pTex;
|
Encoder.mpTexture = pTex;
|
||||||
Encoder.mSourceFormat = ETexelFormat::DXT1;
|
Encoder.mSourceFormat = ETexelFormat::BC1;
|
||||||
Encoder.mOutputFormat = ETexelFormat::GX_CMPR;
|
Encoder.mOutputFormat = ETexelFormat::GX_CMPR;
|
||||||
Encoder.WriteTXTR(rTXTR);
|
Encoder.WriteTXTR(rTXTR);
|
||||||
}
|
}
|
||||||
|
@ -86,10 +87,9 @@ ETexelFormat CTextureEncoder::GetGXFormat(ETexelFormat Format)
|
||||||
{
|
{
|
||||||
case ETexelFormat::Luminance: return ETexelFormat::GX_I8;
|
case ETexelFormat::Luminance: return ETexelFormat::GX_I8;
|
||||||
case ETexelFormat::LuminanceAlpha: return ETexelFormat::GX_IA8;
|
case ETexelFormat::LuminanceAlpha: return ETexelFormat::GX_IA8;
|
||||||
case ETexelFormat::RGBA4: return ETexelFormat::GX_RGB5A3;
|
|
||||||
case ETexelFormat::RGB565: return ETexelFormat::GX_RGB565;
|
case ETexelFormat::RGB565: return ETexelFormat::GX_RGB565;
|
||||||
case ETexelFormat::RGBA8: return ETexelFormat::GX_RGBA8;
|
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;
|
default: return ETexelFormat::Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,8 @@ ETexelFormat CTextureEncoder::GetFormat(ETexelFormat Format)
|
||||||
case ETexelFormat::GX_IA4: return ETexelFormat::LuminanceAlpha;
|
case ETexelFormat::GX_IA4: return ETexelFormat::LuminanceAlpha;
|
||||||
case ETexelFormat::GX_IA8: return ETexelFormat::LuminanceAlpha;
|
case ETexelFormat::GX_IA8: return ETexelFormat::LuminanceAlpha;
|
||||||
// todo rest of these
|
// todo rest of these
|
||||||
case ETexelFormat::GX_CMPR: return ETexelFormat::DXT1;
|
case ETexelFormat::GX_CMPR: return ETexelFormat::BC1;
|
||||||
default: return ETexelFormat::Invalid;
|
default: return ETexelFormat::Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#ifndef CTEXTUREENCODER_H
|
#ifndef CTEXTUREENCODER_H
|
||||||
#define CTEXTUREENCODER_H
|
#define CTEXTUREENCODER_H
|
||||||
|
|
||||||
#include "Core/Resource/CTexture.h"
|
#if 0
|
||||||
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
#include "Core/Resource/TResPtr.h"
|
#include "Core/Resource/TResPtr.h"
|
||||||
|
|
||||||
// Class contains basic functionality right now - only supports directly converting DXT1 to CMPR
|
// 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 GetGXFormat(ETexelFormat Format);
|
||||||
static ETexelFormat GetFormat(ETexelFormat Format);
|
static ETexelFormat GetFormat(ETexelFormat Format);
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // CTEXTUREENCODER_H
|
#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
|
#ifndef CTEXTUREDECODER_H
|
||||||
#define CTEXTUREDECODER_H
|
#define CTEXTUREDECODER_H
|
||||||
|
|
||||||
#include "Core/Resource/CTexture.h"
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
#include "Core/Resource/ETexelFormat.h"
|
#include "Core/Resource/Texture/ETexelFormat.h"
|
||||||
#include <Common/BasicTypes.h>
|
#include <Common/BasicTypes.h>
|
||||||
#include <Common/CColor.h>
|
#include <Common/CColor.h>
|
||||||
|
|
||||||
|
@ -10,86 +10,44 @@
|
||||||
|
|
||||||
class CTextureDecoder
|
class CTextureDecoder
|
||||||
{
|
{
|
||||||
CResourceEntry *mpEntry;
|
CResourceEntry* mpEntry;
|
||||||
ETexelFormat mTexelFormat;
|
CTexture* mpTexture;
|
||||||
uint16 mWidth, mHeight;
|
|
||||||
|
// Texture asset data
|
||||||
|
EGXTexelFormat mTexelFormat;
|
||||||
|
uint16 mSizeX, mSizeY;
|
||||||
uint32 mNumMipMaps;
|
uint32 mNumMipMaps;
|
||||||
|
|
||||||
bool mHasPalettes;
|
// Palette data
|
||||||
EGXPaletteFormat mPaletteFormat;
|
std::vector<uint8> mPaletteData;
|
||||||
std::vector<uint8> mPalettes;
|
uint32 mPaletteTexelStride;
|
||||||
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);
|
|
||||||
|
|
||||||
// Decode
|
// Decode
|
||||||
void PartialDecodeGXTexture(IInputStream& rTXTR);
|
void DecodeGXTexture(IInputStream& TXTR);
|
||||||
void FullDecodeGXTexture(IInputStream& rTXTR);
|
void ParseTexel(EGXTexelFormat Format, IInputStream& Src, IOutputStream& Dst);
|
||||||
void DecodeDDS(IInputStream& rDDS);
|
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)
|
CTextureDecoder(CResourceEntry* pEntry);
|
||||||
void ReadPixelsI4(IInputStream& rSrc, IOutputStream& rDst);
|
~CTextureDecoder();
|
||||||
void ReadPixelI8(IInputStream& rSrc, IOutputStream& rDst);
|
CTexture* ReadTXTR(IInputStream& TXTR);
|
||||||
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);
|
|
||||||
|
|
||||||
// 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:
|
public:
|
||||||
static CTexture* LoadTXTR(IInputStream& rTXTR, CResourceEntry *pEntry);
|
static CTexture* LoadTXTR(IInputStream& TXTR, CResourceEntry* pEntry);
|
||||||
static CTexture* LoadDDS(IInputStream& rDDS, CResourceEntry *pEntry);
|
|
||||||
static CTexture* DoFullDecode(IInputStream& rTXTR, CResourceEntry *pEntry);
|
|
||||||
static CTexture* DoFullDecode(CTexture *pTexture);
|
|
||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
static uint8 Extend3to8(uint8 In);
|
static uint8 Extend3to8(uint8 In);
|
||||||
static uint8 Extend4to8(uint8 In);
|
static uint8 Extend4to8(uint8 In);
|
||||||
static uint8 Extend5to8(uint8 In);
|
static uint8 Extend5to8(uint8 In);
|
||||||
static uint8 Extend6to8(uint8 In);
|
static uint8 Extend6to8(uint8 In);
|
||||||
static uint32 CalculateShiftForMask(uint32 BitMask);
|
|
||||||
static uint32 CalculateMaskBitCount(uint32 BitMask);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CTEXTUREDECODER_H
|
#endif // CTEXTUREDECODER_H
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
#ifndef RESOURCES_H
|
#ifndef RESOURCES_H
|
||||||
#define RESOURCES_H
|
#define RESOURCES_H
|
||||||
|
|
||||||
|
#include "CAudioGroup.h"
|
||||||
|
#include "CAudioLookupTable.h"
|
||||||
|
#include "CAudioMacro.h"
|
||||||
#include "CDependencyGroup.h"
|
#include "CDependencyGroup.h"
|
||||||
#include "CFont.h"
|
#include "CFont.h"
|
||||||
|
#include "CMapArea.h"
|
||||||
#include "CPoiToWorld.h"
|
#include "CPoiToWorld.h"
|
||||||
#include "CResource.h"
|
#include "CResource.h"
|
||||||
#include "CTexture.h"
|
#include "CStringList.h"
|
||||||
#include "CWorld.h"
|
#include "CWorld.h"
|
||||||
#include "Core/Resource/Animation/CAnimation.h"
|
#include "Core/Resource/Animation/CAnimation.h"
|
||||||
#include "Core/Resource/Animation/CAnimSet.h"
|
#include "Core/Resource/Animation/CAnimSet.h"
|
||||||
|
@ -16,6 +20,7 @@
|
||||||
#include "Core/Resource/Model/CModel.h"
|
#include "Core/Resource/Model/CModel.h"
|
||||||
#include "Core/Resource/Scan/CScan.h"
|
#include "Core/Resource/Scan/CScan.h"
|
||||||
#include "Core/Resource/StringTable/CStringTable.h"
|
#include "Core/Resource/StringTable/CStringTable.h"
|
||||||
|
#include "Core/Resource/Texture/CTexture.h"
|
||||||
|
|
||||||
#endif // RESOURCES_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->AnimParamCSpinBox->setProperty ("ModelEditorWidgetType", (int) EModelEditorWidget::AnimParamCSpinBox);
|
||||||
ui->AnimParamDSpinBox->setProperty ("ModelEditorWidgetType", (int) EModelEditorWidget::AnimParamDSpinBox);
|
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->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->MeshPreviewButton, SIGNAL(clicked()), this, SLOT(SetMeshPreview()));
|
||||||
connect(ui->SpherePreviewButton, SIGNAL(clicked()), this, SLOT(SetSpherePreview()));
|
connect(ui->SpherePreviewButton, SIGNAL(clicked()), this, SLOT(SetSpherePreview()));
|
||||||
connect(ui->FlatPreviewButton, SIGNAL(clicked()), this, SLOT(SetFlatPreview()));
|
connect(ui->FlatPreviewButton, SIGNAL(clicked()), this, SLOT(SetFlatPreview()));
|
||||||
|
@ -765,55 +762,6 @@ void CModelEditorWindow::Import()
|
||||||
gpResourceStore->DestroyUnreferencedResources();
|
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()
|
void CModelEditorWindow::SetMeshPreview()
|
||||||
{
|
{
|
||||||
ui->Viewport->SetDrawMode(CModelEditorViewport::EDrawMode::DrawMesh);
|
ui->Viewport->SetDrawMode(CModelEditorViewport::EDrawMode::DrawMesh);
|
||||||
|
|
|
@ -101,8 +101,6 @@ private:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void Import();
|
void Import();
|
||||||
void ConvertToDDS();
|
|
||||||
void ConvertToTXTR();
|
|
||||||
void SetMeshPreview();
|
void SetMeshPreview();
|
||||||
void SetSpherePreview();
|
void SetSpherePreview();
|
||||||
void SetFlatPreview();
|
void SetFlatPreview();
|
||||||
|
|
|
@ -172,9 +172,9 @@
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>-489</y>
|
<y>0</y>
|
||||||
<width>308</width>
|
<width>319</width>
|
||||||
<height>1184</height>
|
<height>1302</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
@ -2407,7 +2407,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1280</width>
|
<width>1280</width>
|
||||||
<height>21</height>
|
<height>20</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
|
@ -2415,17 +2415,8 @@
|
||||||
<string>File</string>
|
<string>File</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="ActionSave"/>
|
<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>
|
</widget>
|
||||||
<addaction name="menuFile"/>
|
<addaction name="menuFile"/>
|
||||||
<addaction name="menuTextures"/>
|
|
||||||
</widget>
|
</widget>
|
||||||
<action name="ActionSave">
|
<action name="ActionSave">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
|
|
Loading…
Reference in New Issue