3521 lines
89 KiB
C++
3521 lines
89 KiB
C++
/*=============================================================================
|
|
TexMan.cpp : Common Texture manager implementation.
|
|
Copyright (c) 2001 Crytek Studios. All Rights Reserved.
|
|
|
|
Revision history:
|
|
* Created by Khonich Andrey
|
|
|
|
=============================================================================*/
|
|
|
|
#include "RenderPCH.h"
|
|
#include "../CommonRender.h"
|
|
|
|
#ifdef USE_3DC
|
|
#include "../3Dc/CompressorLib.h"
|
|
#endif
|
|
|
|
#ifdef PS2
|
|
#include "DXTCImage.h"
|
|
#include "S3TC.h"
|
|
#endif
|
|
#if defined(LINUX)
|
|
#include "ILog.h"
|
|
#endif
|
|
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
|
|
int CTexMan::m_CurStage;
|
|
int CTexMan::m_nCurStages;
|
|
|
|
STexPic STexPic::m_Root;
|
|
|
|
char *texdir = "Textures";
|
|
|
|
CTexMan::CTexMan()
|
|
{
|
|
m_bRGBA = true;
|
|
m_MinFilter = 0;
|
|
m_MagFilter = 0;
|
|
m_LastTex = NULL;
|
|
m_CurStage = 0;
|
|
m_Streamed = CRenderer::CV_r_texturesstreaming;
|
|
m_CurTexMaxSize = CRenderer::CV_r_texmaxsize;
|
|
m_CurTexSkyResolution = CRenderer::CV_r_texskyresolution;
|
|
m_CurTexSkyQuality = CRenderer::CV_r_texskyquality;
|
|
m_CurTexResolution = CRenderer::CV_r_texresolution;
|
|
#ifdef USE_3DC
|
|
m_CurTexNormalMapCompressed = CRenderer::CV_r_texnormalmapcompressed;
|
|
#endif
|
|
m_CurTexQuality = CRenderer::CV_r_texquality;
|
|
m_CurTexBumpResolution = CRenderer::CV_r_texbumpresolution;
|
|
m_CurTexBumpQuality = CRenderer::CV_r_texbumpquality;
|
|
if (!m_FreeTexPoolItems.m_NextFree)
|
|
{
|
|
m_FreeTexPoolItems.m_NextFree = &m_FreeTexPoolItems;
|
|
m_FreeTexPoolItems.m_PrevFree = &m_FreeTexPoolItems;
|
|
}
|
|
m_nTexSizeHistory = 0;
|
|
m_nProcessedTextureID1 = 0;
|
|
m_pProcessedTexture1 = NULL;
|
|
m_nProcessedTextureID2 = 0;
|
|
m_pProcessedTexture2 = NULL;
|
|
m_nPhaseProcessingTextures = 0;
|
|
m_nCustomMip = 0;
|
|
}
|
|
|
|
void CTexMan::Shutdown()
|
|
{
|
|
int i;
|
|
|
|
if(GetISystem()->GetIRenderer()->GetType()==R_NULL_RENDERER) // workaround to fix crash when quitting the dedicated server - because the textures are shared
|
|
return; // should be fixed soon
|
|
|
|
SAFE_RELEASE_FORCE(m_Text_White);
|
|
SAFE_RELEASE_FORCE(m_Text_WhiteShadow);
|
|
SAFE_RELEASE_FORCE(m_Text_WhiteBump);
|
|
SAFE_RELEASE_FORCE(m_Text_Gradient);
|
|
SAFE_RELEASE_FORCE(m_Text_Depth);
|
|
SAFE_RELEASE_FORCE(m_Text_Atten2D);
|
|
SAFE_RELEASE_FORCE(m_Text_Edge);
|
|
SAFE_RELEASE_FORCE(m_Text_NoiseVolumeMap);
|
|
SAFE_RELEASE_FORCE(m_Text_NormalizeCMap);
|
|
SAFE_RELEASE_FORCE(m_Text_EnvLCMap);
|
|
SAFE_RELEASE_FORCE(m_Text_EnvTex);
|
|
SAFE_RELEASE_FORCE(m_Text_EnvScr);
|
|
SAFE_RELEASE_FORCE(m_Text_Glare);
|
|
SAFE_RELEASE_FORCE(m_Text_HeatMap);
|
|
SAFE_RELEASE_FORCE(m_Text_RefractMap);
|
|
SAFE_RELEASE_FORCE(m_Text_WaterMap);
|
|
SAFE_RELEASE_FORCE(m_Text_MotionBlurMap);
|
|
SAFE_RELEASE_FORCE(m_Text_NightVisMap);
|
|
SAFE_RELEASE_FORCE(m_Text_FlashBangMap);
|
|
SAFE_RELEASE_FORCE(m_Text_RainMap);
|
|
SAFE_RELEASE_FORCE(m_Text_LightCMap);
|
|
for (i=0; i<8; i++)
|
|
{
|
|
SAFE_RELEASE_FORCE(m_Text_FromRE[i]);
|
|
}
|
|
SAFE_RELEASE_FORCE(m_Text_FromObj);
|
|
SAFE_RELEASE_FORCE(m_Text_FromLight);
|
|
SAFE_RELEASE_FORCE(m_Text_Fog);
|
|
SAFE_RELEASE_FORCE(m_Text_Fog_Enter);
|
|
SAFE_RELEASE_FORCE(m_Text_VFog);
|
|
SAFE_RELEASE_FORCE(m_Text_Flare);
|
|
SAFE_RELEASE_FORCE(m_Text_Ghost);
|
|
SAFE_RELEASE_FORCE(m_Text_DepthLookup);
|
|
SAFE_RELEASE_FORCE(m_Text_FlashBangFlash);
|
|
SAFE_RELEASE_FORCE(m_Text_ScreenNoise);
|
|
SAFE_RELEASE_FORCE(m_Text_HeatPalete);
|
|
SAFE_RELEASE_FORCE(m_Text_ScreenMap);
|
|
SAFE_RELEASE_FORCE(m_Text_PrevScreenMap);
|
|
|
|
SAFE_RELEASE_FORCE(m_Text_ScreenLuminosityMap);
|
|
SAFE_RELEASE_FORCE(m_Text_ScreenCurrLuminosityMap);
|
|
SAFE_RELEASE_FORCE(m_Text_ScreenLowMap);
|
|
SAFE_RELEASE_FORCE(m_Text_ScreenAvg1x1);
|
|
SAFE_RELEASE_FORCE(m_Text_DofMap);
|
|
|
|
for (i=0; i<MAX_ENVLIGHTCUBEMAPS; i++)
|
|
{
|
|
SAFE_RELEASE_FORCE(m_EnvLCMaps[i].m_Tex);
|
|
}
|
|
for (i=0; i<MAX_ENVCUBEMAPS; i++)
|
|
{
|
|
SAFE_RELEASE_FORCE(m_EnvCMaps[i].m_Tex);
|
|
}
|
|
for (i=0; i<MAX_ENVTEXTURES; i++)
|
|
{
|
|
SAFE_RELEASE_FORCE(m_EnvTexts[i].m_Tex);
|
|
}
|
|
for (i=0; i<16; i++)
|
|
{
|
|
SAFE_RELEASE_FORCE(m_CustomCMaps[i].m_Tex);
|
|
}
|
|
for (i=0; i<16; i++)
|
|
{
|
|
SAFE_RELEASE_FORCE(m_CustomTextures[i].m_Tex);
|
|
}
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
SAFE_RELEASE_FORCE(m_Text_Gray);
|
|
SAFE_RELEASE_FORCE(m_Text_NoTexture);
|
|
}
|
|
|
|
void CTexMan::ReloadTextures()
|
|
{
|
|
for (int i=0; i<m_Textures.Num(); i++)
|
|
{
|
|
STexPic *tp = m_Textures[i];
|
|
if (!tp)
|
|
continue;
|
|
if (!tp->m_bBusy)
|
|
continue;
|
|
if (tp->m_Flags2 & FT2_WASLOADED)
|
|
{
|
|
gRenDev->EF_LoadTexture(tp->m_SearchName.c_str(), tp->m_Flags, tp->m_Flags2 | FT2_RELOAD, tp->m_eTT, tp->m_fAmount1, tp->m_fAmount2, tp->m_Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTexMan::IsTextureLoaded(const char *pName)
|
|
{
|
|
STexPic *tp = GetByName(pName);
|
|
if (!tp)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
STexPic *CTexMan::GetByName(const char *pName)
|
|
{
|
|
STexLoaded *tl = NULL;
|
|
CName nmf(pName, eFN_Find);
|
|
if (!nmf.GetIndex())
|
|
return NULL;
|
|
LoadedTexsMapItor it = m_TexsMap.find(nmf.GetIndex());
|
|
if (it != m_TexsMap.end())
|
|
{
|
|
tl = it->second;
|
|
if (tl->m_NumTextures)
|
|
return tl->m_Textures[0];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int STexPic::GetFileNames(char *name0, char *name1, int nLen)
|
|
{
|
|
const char *name = strstr(m_SourceName.c_str(), "+norm_");
|
|
if (!name)
|
|
{
|
|
name = strchr(m_SourceName.c_str(), '$');
|
|
if (name)
|
|
{
|
|
const char *name1 = strchr(&name[1], '$');
|
|
if (name1)
|
|
{
|
|
nLen = min(nLen, name1-name-1);
|
|
strncpy(name0, &name[1], nLen);
|
|
name0[nLen] = 0;
|
|
return 1;
|
|
}
|
|
}
|
|
strncpy(name0, m_SourceName.c_str(), nLen);
|
|
return 1;
|
|
}
|
|
int nL = min(nLen, name-m_SourceName.c_str());
|
|
strncpy(name0, m_SourceName.c_str(), nL);
|
|
name0[nL] = 0;
|
|
nL = min(nLen, (int)strlen(&name[6]));
|
|
strncpy(name1, &name[6], nL);
|
|
name1[nL] = 0;
|
|
|
|
return 2;
|
|
}
|
|
|
|
void STexPic::Release(int bForce)
|
|
{
|
|
//gRenDev->m_TexMan->ValidateTexSize();
|
|
|
|
if (m_Bind == TX_FIRSTBIND && m_Id < 4 && m_Id != 0)
|
|
{
|
|
if (bForce == 2)
|
|
{
|
|
RemoveFromSearchHash();
|
|
gRenDev->m_TexMan->m_Textures[m_Id] = NULL;
|
|
if (m_Id != gRenDev->m_TexMan->m_Textures.Num()-1)
|
|
gRenDev->m_TexMan->m_FreeSlots.AddElem(m_Id);
|
|
|
|
Unlink();
|
|
delete this;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!bForce)
|
|
{
|
|
if (m_Flags & FT_NOREMOVE)
|
|
return;
|
|
m_nRefCounter--;
|
|
if (m_nRefCounter)
|
|
return;
|
|
}
|
|
|
|
SAFE_DELETE_ARRAY(m_pData);
|
|
SAFE_DELETE_ARRAY(m_pData32);
|
|
SAFE_DELETE_ARRAY(m_Matrix);
|
|
if (m_pPalette)
|
|
{
|
|
if (m_pPalette != &gRenDev->m_TexMan->m_NMPalette[0][0])
|
|
delete [] m_pPalette;
|
|
m_pPalette = NULL;
|
|
}
|
|
SAFE_DELETE_ARRAY(m_p8to24table);
|
|
SAFE_DELETE_ARRAY(m_p15to8table);
|
|
SAFE_DELETE(m_pFuncMap);
|
|
SAFE_DELETE(m_pFileTexMips);
|
|
SAFE_DELETE(m_pSH);
|
|
|
|
if (m_pPoolItem)
|
|
m_pPoolItem->LinkFree(&gRenDev->m_TexMan->m_FreeTexPoolItems);
|
|
RemoveFromPool();
|
|
RemoveMips(0);
|
|
|
|
if (m_Bind != TX_FIRSTBIND || bForce == 2)
|
|
gRenDev->m_TexMan->RemoveFromHash(m_Bind, this);
|
|
|
|
RemoveFromSearchHash();
|
|
|
|
ReleaseDriverTexture();
|
|
|
|
gRenDev->m_TexMan->m_Textures[m_Id] = NULL;
|
|
gRenDev->m_TexMan->m_FreeSlots.AddElem(m_Id);
|
|
|
|
Unlink();
|
|
|
|
//gRenDev->m_TexMan->ValidateTexSize();
|
|
|
|
delete this;
|
|
}
|
|
|
|
void STexPic::RemoveFromSearchHash()
|
|
{
|
|
STexLoaded *tl = m_TL;
|
|
int i;
|
|
for (i=0; i<tl->m_NumTextures; i++)
|
|
{
|
|
if (this == tl->m_Textures[i])
|
|
break;
|
|
}
|
|
if (i != tl->m_NumTextures)
|
|
{
|
|
int j = 0;
|
|
STexPic *tp[8];
|
|
for (i=0; i<tl->m_NumTextures; i++)
|
|
{
|
|
if (this == tl->m_Textures[i])
|
|
continue;
|
|
tp[j] = tl->m_Textures[i];
|
|
j++;
|
|
}
|
|
tl->m_NumTextures = j;
|
|
for (int i=0; i<j; i++)
|
|
{
|
|
tl->m_Textures[i] = tp[i];
|
|
}
|
|
}
|
|
if (!tl->m_NumTextures)
|
|
{
|
|
gRenDev->m_TexMan->m_TexsMap.erase(m_SearchName.GetIndex());
|
|
delete tl;
|
|
}
|
|
else
|
|
{
|
|
/*#ifdef _DEBUG
|
|
for (int nn=0; nn<tl->m_NumTextures; nn++)
|
|
{
|
|
for (int nnn=nn+1; nnn<tl->m_NumTextures; nnn++)
|
|
{
|
|
if (tl->m_Textures[nn]->m_eTT == tl->m_Textures[nnn]->m_eTT)
|
|
assert(0);
|
|
}
|
|
}
|
|
#endif*/
|
|
}
|
|
m_TL = NULL;
|
|
}
|
|
|
|
void STexPic::AddToSearchHash()
|
|
{
|
|
CName nmf = m_SearchName;
|
|
LoadedTexsMapItor it = gRenDev->m_TexMan->m_TexsMap.find(nmf.GetIndex());
|
|
STexLoaded *tl;
|
|
if (it != gRenDev->m_TexMan->m_TexsMap.end())
|
|
{
|
|
tl = it->second;
|
|
int j;
|
|
for (j=0; j<tl->m_NumTextures; j++)
|
|
{
|
|
if (j == 7)
|
|
{
|
|
Warning( VALIDATOR_FLAG_TEXTURE,*nmf,"Too many texture types for name '%s'\n", *nmf);
|
|
j = 6;
|
|
break;
|
|
}
|
|
if (this == tl->m_Textures[j])
|
|
break;
|
|
}
|
|
if (j == tl->m_NumTextures)
|
|
{
|
|
tl->m_NumTextures = j+1;
|
|
tl->m_Textures[j] = this;
|
|
}
|
|
m_TL = tl;
|
|
}
|
|
else
|
|
{
|
|
tl = new STexLoaded;
|
|
tl->m_NumTextures = 1;
|
|
tl->m_Textures[0] = this;
|
|
gRenDev->m_TexMan->m_TexsMap.insert(LoadedTexsMapItor::value_type(nmf.GetIndex(), tl));
|
|
m_TL = tl;
|
|
}
|
|
assert(tl->m_NumTextures < 4);
|
|
|
|
#ifdef _DEBUG
|
|
for (int nn=0; nn<tl->m_NumTextures; nn++)
|
|
{
|
|
for (int nnn=nn+1; nnn<tl->m_NumTextures; nnn++)
|
|
{
|
|
if (tl->m_Textures[nn]->m_eTT == tl->m_Textures[nnn]->m_eTT)
|
|
assert(0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//extern TArray<STexPic *> sTestStr;
|
|
//extern TArray<STexPic *> sTestTx;
|
|
|
|
void CTexMan::ValidateTexSize()
|
|
{
|
|
return;
|
|
int nSize = 0;
|
|
STexPic *pTP = STexPic::m_Root.m_Prev;
|
|
//sTestTx.Free();
|
|
while (pTP != &STexPic::m_Root)
|
|
{
|
|
if (pTP->m_LoadedSize >= 0)
|
|
{
|
|
if (pTP->m_LoadedSize)
|
|
nSize += pTP->m_LoadedSize;
|
|
else
|
|
nSize += pTP->m_Size;
|
|
}
|
|
|
|
//sTestTx.AddElem(pTP);
|
|
pTP = pTP->m_Prev;
|
|
}
|
|
STexPoolItem *pIT = gRenDev->m_TexMan->m_FreeTexPoolItems.m_PrevFree;
|
|
while (pIT != &gRenDev->m_TexMan->m_FreeTexPoolItems)
|
|
{
|
|
nSize += pIT->m_pOwner->m_Size;
|
|
pIT = pIT->m_PrevFree;
|
|
}
|
|
assert(nSize == m_StatsCurTexMem);
|
|
}
|
|
|
|
void sLogTexture (const char *name, int Size)
|
|
{
|
|
struct STexLogs
|
|
{
|
|
char Name[256];
|
|
};
|
|
static STexLogs *sTexLogs = NULL;
|
|
static int sNumTexLogs = 0;
|
|
static FILE *fp = NULL;
|
|
if (!sTexLogs)
|
|
{
|
|
sTexLogs = new STexLogs[2048];
|
|
fp = fxopen("UsedTextures.txt", "w");
|
|
}
|
|
int i;
|
|
for (i=0; i<sNumTexLogs; i++)
|
|
{
|
|
if (!stricmp(sTexLogs[i].Name, name))
|
|
break;
|
|
}
|
|
if (i == sNumTexLogs)
|
|
{
|
|
strcpy(sTexLogs[i].Name, name);
|
|
sNumTexLogs++;
|
|
fprintf(fp, "%s (%d)\n", name, Size);
|
|
}
|
|
}
|
|
|
|
#define GL_TEXTURE_2D 0x0DE1
|
|
#define GL_TEXTURE_3D 0x806F
|
|
#define GL_TEXTURE_CUBE_MAP_EXT 0x8513
|
|
#define GL_TEXTURE_RECTANGLE_NV 0x84F5
|
|
|
|
STexPic *CTexMan::TextureInfoForName(const char *nameTex, int numT, byte eTT, uint flags, uint flags2, int bind)
|
|
{
|
|
char fullnm[256];
|
|
int i;
|
|
|
|
STexPic *ti = NULL;
|
|
STexPic *tiFound = NULL;
|
|
STexLoaded *tl = NULL;
|
|
|
|
if (!nameTex)
|
|
{
|
|
Warning( VALIDATOR_FLAG_TEXTURE,0,"CTexMan::TextureInfoForName: NULL name\n");
|
|
return NULL;
|
|
}
|
|
//iLog->Log("TexInfo: %s", nameTex);
|
|
|
|
if (!(flags2 & FT2_DISCARDINCACHE))
|
|
StripExtension(nameTex, fullnm);
|
|
else
|
|
strcpy(fullnm, nameTex);
|
|
ConvertDOSToUnixName(fullnm, fullnm);
|
|
|
|
CName nmf = CName(fullnm);
|
|
if (numT>=0)
|
|
{
|
|
i = numT;
|
|
if (!m_Textures[i]->m_bBusy)
|
|
goto create;
|
|
if (nmf == m_Textures[i]->m_SearchName)
|
|
return m_Textures[i];
|
|
if (m_Textures[i]->m_eTT != eTT_Cubemap && m_Textures[i]->m_Bind > TX_FIRSTBIND || m_Textures[i]->m_Bind < 0xf00)
|
|
Warning( VALIDATOR_FLAG_TEXTURE,fullnm,"Error custom texture definition for '%s'\n", fullnm);
|
|
else
|
|
if (m_Textures[i]->m_eTT == eTT_Cubemap)
|
|
m_Textures[i]->m_SearchName = nmf;
|
|
return m_Textures[i];
|
|
}
|
|
|
|
if (flags & FT_NODOWNLOAD)
|
|
i = m_Textures.Num();
|
|
else
|
|
{
|
|
LoadedTexsMapItor it = m_TexsMap.find(nmf.GetIndex());
|
|
if (it != m_TexsMap.end())
|
|
{
|
|
tl = it->second;
|
|
for (i=0; i<tl->m_NumTextures; i++)
|
|
{
|
|
STexPic *ti = tl->m_Textures[i];
|
|
if (ti->m_bBusy)
|
|
{
|
|
if ((flags ^ ti->m_Flags) & FT_DYNAMIC)
|
|
continue;
|
|
if ((flags ^ ti->m_Flags) & FT_NORESIZE)
|
|
continue;
|
|
if ((flags ^ ti->m_Flags) & (FT_3DC_A | FT_ALLOW3DC))
|
|
continue;
|
|
if ((flags2 ^ ti->m_Flags2) & (FT2_REPLICATETOALLSIDES | FT2_CHECKFORALLSEQUENCES))
|
|
continue;
|
|
if (eTT != ti->m_eTT)
|
|
continue;
|
|
if (bind > 0 && ti->m_Bind != bind)
|
|
continue;
|
|
ti->m_nRefCounter++;
|
|
return ti;
|
|
}
|
|
else
|
|
if (!tiFound)
|
|
tiFound = ti;
|
|
}
|
|
if (tiFound)
|
|
i = tiFound->m_Id;
|
|
else
|
|
i = m_Textures.Num();
|
|
}
|
|
else
|
|
i = m_Textures.Num();
|
|
}
|
|
if (i == m_Textures.Num())
|
|
{
|
|
int n;
|
|
if (n = m_FreeSlots.Num())
|
|
{
|
|
i = m_FreeSlots[n-1];
|
|
m_FreeSlots.Remove(n-1, 1);
|
|
}
|
|
}
|
|
|
|
create:
|
|
if (i>=m_Textures.Num() || !m_Textures[i])
|
|
{
|
|
ti = CreateTexture();
|
|
if (i < m_Textures.Num())
|
|
m_Textures[i] = ti;
|
|
else
|
|
m_Textures.AddElem(ti);
|
|
}
|
|
|
|
ti = m_Textures[i];
|
|
if (tl)
|
|
{
|
|
int j;
|
|
for (j=0; j<tl->m_NumTextures; j++)
|
|
{
|
|
if (ti == tl->m_Textures[j])
|
|
break;
|
|
if (j == 6)
|
|
{
|
|
assert(0);
|
|
Warning( VALIDATOR_FLAG_TEXTURE,fullnm,"Too many texture types for name '%s'\n", fullnm);
|
|
break;
|
|
}
|
|
}
|
|
if (j == tl->m_NumTextures)
|
|
{
|
|
tl->m_NumTextures = j+1;
|
|
tl->m_Textures[j] = ti;
|
|
assert(tl->m_NumTextures < 8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tl = new STexLoaded;
|
|
tl->m_NumTextures = 1;
|
|
tl->m_Textures[0] = ti;
|
|
m_TexsMap.insert(LoadedTexsMapItor::value_type(nmf.GetIndex(), tl));
|
|
}
|
|
ti->m_SearchName = nmf;
|
|
ti->m_Name = fullnm;
|
|
ti->m_SourceName = fullnm;
|
|
ti->m_eTT = (ETexType)eTT;
|
|
ti->m_bBusy = false;
|
|
ti->m_Id = i;
|
|
ti->m_nRefCounter = 1;
|
|
ti->m_TL = tl;
|
|
/*#ifdef _DEBUG
|
|
for (int nn=0; nn<tl->m_NumTextures; nn++)
|
|
{
|
|
for (int nnn=nn+1; nnn<tl->m_NumTextures; nnn++)
|
|
{
|
|
if (tl->m_Textures[nn]->m_eTT == tl->m_Textures[nnn]->m_eTT)
|
|
assert(0);
|
|
}
|
|
}
|
|
#endif*/
|
|
|
|
if (ti->m_eTT == eTT_Cubemap)
|
|
ti->m_TargetType = GL_TEXTURE_CUBE_MAP_EXT;
|
|
else
|
|
if (ti->m_eTT == eTT_Rectangle)
|
|
ti->m_TargetType = GL_TEXTURE_RECTANGLE_NV;
|
|
else
|
|
if (ti->m_eTT == eTT_3D)
|
|
ti->m_TargetType = GL_TEXTURE_3D;
|
|
else
|
|
ti->m_TargetType = GL_TEXTURE_2D;
|
|
|
|
return ti;
|
|
}
|
|
|
|
#ifdef WIN64
|
|
#pragma warning( push ) //AMD Port
|
|
#pragma warning( disable : 4267 ) // conversion from 'size_t' to 'int', possible loss of data
|
|
#endif
|
|
|
|
static inline void sAddTexToArray(TArray<STexPic *>& arrTex, STexPic *tx)
|
|
{
|
|
if (tx && tx->m_NextCMSide)
|
|
{
|
|
assert(tx->m_NextCMSide != tx);
|
|
sAddTexToArray(arrTex, tx->m_NextCMSide);
|
|
tx->m_NextCMSide = NULL;
|
|
}
|
|
if (tx)
|
|
arrTex.AddElem(tx);
|
|
}
|
|
|
|
static inline void sRemoveGarbage(STexPic *tx)
|
|
{
|
|
int i;
|
|
TArray<STexPic *> arrTexs;
|
|
sAddTexToArray(arrTexs, tx->m_NextCMSide);
|
|
tx->m_NextCMSide = NULL;
|
|
for (i=0; i<arrTexs.Num(); i++)
|
|
{
|
|
arrTexs[i]->Release(false);
|
|
}
|
|
}
|
|
|
|
STexPic *CTexMan::LoadCubeTex(const char *mapname, uint flags, uint flags2, int State, byte eTT, int RState, int Id, int BindId, float fAmount)
|
|
{
|
|
static char* cubefaces[6] = {"posx","negx","posy","negy","posz","negz"};
|
|
char name[256];
|
|
StripExtension(mapname, name);
|
|
int len = strlen(name);
|
|
if (len > 5)
|
|
{
|
|
for (int i=0; i<6; i++)
|
|
{
|
|
if (!stricmp(&name[len-4], cubefaces[i]))
|
|
{
|
|
if (name[len-5] == '_')
|
|
len--;
|
|
name[len-4] = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int tId = Id;
|
|
int tbId = BindId;
|
|
char cube[256];
|
|
STexPic *tx;
|
|
for (int i=0; i<6; i++)
|
|
{
|
|
sprintf(cube, "%s_%s", name, cubefaces[i]);
|
|
STexPic *ti = NULL;
|
|
if (!(flags2 & FT2_RELOAD) || !(flags2 & FT2_CUBEASSINGLETEXTURE))
|
|
ti = LoadTexture(cube, flags, flags2, eTT, fAmount, fAmount, tbId, tId);
|
|
// if we can't load all 6 sides of cube texture
|
|
// try to create cube-map from one texture (if FT2_FORCECUBEMAP specified)
|
|
if (!ti || (ti->m_Flags & FT_NOTFOUND))
|
|
{
|
|
if (flags2 & FT2_FORCECUBEMAP)
|
|
{
|
|
int nNumSeq = -1;
|
|
if (!(flags2 & FT2_RELOAD))
|
|
ti->Release(0);
|
|
StripExtension(mapname, name);
|
|
int len = strlen(name);
|
|
for (int j=0; j<6; j++)
|
|
{
|
|
if (!stricmp(&name[len-4], cubefaces[j]))
|
|
{
|
|
if (name[len-5] == '_')
|
|
len--;
|
|
name[len-4] = 0;
|
|
break;
|
|
}
|
|
}
|
|
STexPic *pPrevTex = NULL;
|
|
STexPic *pTX = NULL;
|
|
int n;
|
|
while (true)
|
|
{
|
|
ti = LoadTexture(name, flags, flags2, eTT, fAmount, fAmount, BindId, Id);
|
|
if (ti->m_Flags & FT_NOTFOUND)
|
|
{
|
|
if (pTX)
|
|
return pTX;
|
|
return ti;
|
|
}
|
|
if (ti->m_Flags2 & FT2_WASFOUND)
|
|
{
|
|
if ((ti->m_Flags2 & FT2_REPLICATETOALLSIDES) == (flags2 & FT2_REPLICATETOALLSIDES))
|
|
{
|
|
if (!(flags2 & FT2_CHECKFORALLSEQUENCES))
|
|
{
|
|
ti->m_NextTxt = NULL;
|
|
return ti;
|
|
}
|
|
else
|
|
{
|
|
if (ti->m_NextTxt)
|
|
{
|
|
if (pPrevTex)
|
|
pPrevTex->m_NextTxt = ti;
|
|
return ti;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m_CurCubemapBind = ti->m_Bind;
|
|
}
|
|
/*else
|
|
{
|
|
if (ti->m_Flags2 & FT2_WASUNLOADED)
|
|
return ti;
|
|
}*/
|
|
tx = ti;
|
|
if (nNumSeq < 0)
|
|
{
|
|
tx->m_Flags2 &= ~FT2_REPLICATETOALLSIDES;
|
|
tx->m_Flags2 |= (flags2 & FT2_REPLICATETOALLSIDES);
|
|
pTX = tx;
|
|
}
|
|
ti->m_Flags2 |= FT2_CUBEASSINGLETEXTURE;
|
|
if (ti->m_Flags & FT_DXT)
|
|
{
|
|
flags2 |= FT2_FORCEDXT;
|
|
}
|
|
if (flags2 & FT2_REPLICATETOALLSIDES)
|
|
{
|
|
tx->m_Flags2 |= FT2_REPLICATETOALLSIDES;
|
|
if (!(tx->m_Flags2 & (FT2_WASUNLOADED | FT2_PARTIALLYLOADED)))
|
|
{
|
|
STexPic *pPrevTex = tx;
|
|
for (int j=1; j<6; j++)
|
|
{
|
|
sprintf(cube, "%s_%s", name, cubefaces[j]);
|
|
ti = gRenDev->m_TexMan->CopyTexture(cube, tx, j);
|
|
if (ti)
|
|
{
|
|
ti->m_ETF = tx->m_ETF;
|
|
pPrevTex->m_NextCMSide = ti;
|
|
pPrevTex = ti;
|
|
}
|
|
}
|
|
sRemoveGarbage(tx);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(tx->m_Flags2 & (FT2_WASUNLOADED | FT2_PARTIALLYLOADED)))
|
|
{
|
|
int size = ti->m_Width * ti->m_Height * 4;
|
|
byte *data = new byte [size];
|
|
memset(data, 0, size);
|
|
if (ti->m_ETF == eTF_DXT3 || ti->m_ETF == eTF_DXT5)
|
|
flags |= FT_HASALPHA;
|
|
for (int j=1; j<6; j++)
|
|
{
|
|
sprintf(cube, "%s_%s", name, cubefaces[j]);
|
|
ti = gRenDev->m_TexMan->CreateTexture(cube, ti->m_Width, ti->m_Height, 1, flags, flags2, data, eTT_Cubemap, fAmount);
|
|
ti->m_ETF = tx->m_ETF;
|
|
}
|
|
delete [] data;
|
|
}
|
|
}
|
|
if (!(flags2 & FT2_CHECKFORALLSEQUENCES))
|
|
{
|
|
sRemoveGarbage(pTX);
|
|
return pTX;
|
|
}
|
|
char name01[256];
|
|
if (nNumSeq < 0)
|
|
{
|
|
int len = strlen(name);
|
|
n = 1;
|
|
while (isdigit(name[len-n]))
|
|
{
|
|
n++;
|
|
}
|
|
nNumSeq = atoi(&name[len-n+1]);
|
|
name[len-n+1] = 0;
|
|
strcpy(name01, name);
|
|
}
|
|
if (pPrevTex)
|
|
pPrevTex->m_NextTxt = tx;
|
|
pPrevTex = tx;
|
|
nNumSeq++;
|
|
if (n == 2)
|
|
sprintf(name, "%s%01d", name01, nNumSeq);
|
|
else
|
|
sprintf(name, "%s%02d", name01, nNumSeq);
|
|
}
|
|
sRemoveGarbage(pTX);
|
|
return pTX;
|
|
}
|
|
sRemoveGarbage(ti);
|
|
return ti;
|
|
}
|
|
if (!i)
|
|
{
|
|
if (ti->m_Flags2 & FT2_WASFOUND)
|
|
return ti;
|
|
//if (!(flags2 & FT2_NODXT) && !(ti->m_Flags & FT_DXT))
|
|
// flags2 |= FT2_GENERATEDDS;
|
|
if (!(ti->m_Flags & FT_DXT))
|
|
flags2 |= FT2_NODXT;
|
|
tx = ti;
|
|
}
|
|
if (flags2 & FT2_RELOAD)
|
|
{
|
|
tId = -1;
|
|
tbId = 0;
|
|
}
|
|
}
|
|
//tx->SaveJPG("BugCube", false);
|
|
sRemoveGarbage(tx);
|
|
return tx;
|
|
}
|
|
|
|
#ifdef WIN64
|
|
#pragma warning( pop ) //AMD Port
|
|
#endif
|
|
|
|
|
|
STexPic *CTexMan::LoadTexture(const char* nameTex, uint flags, uint flags2, byte eTT, float fAmount1, float fAmount2, int bind, int numT)
|
|
{
|
|
int num = 0;
|
|
int i;
|
|
byte *src, *dst, *src1;
|
|
char nametex[256];
|
|
|
|
src1 = NULL;
|
|
src = dst = NULL;
|
|
|
|
STexPic *ti = TextureInfoForName(nameTex, numT, eTT, flags, flags2, bind);
|
|
/*if (gRenDev->m_TexMan->m_Text_NoTexture)
|
|
{
|
|
char name[256];
|
|
int Tgt = ti->m_TargetType;
|
|
|
|
ti->m_bBusy = true;
|
|
ti->m_LoadedSize = 0;
|
|
i = ti->m_Id;
|
|
strcpy(name, *ti->m_SearchName);
|
|
STexLoaded *tl = ti->m_TL;
|
|
if (m_Textures[0])
|
|
*ti = *(m_Textures[0]);
|
|
if (!bind)
|
|
ti->m_Bind = TX_FIRSTBIND + i;
|
|
else
|
|
ti->m_Bind = bind;
|
|
ti->m_eTT = (ETexType)eTT;
|
|
ti->m_TL = tl;
|
|
ti->m_Name = nametex;
|
|
ti->m_SearchName = name;
|
|
ti->m_SourceName = name;
|
|
ti->m_TargetType = Tgt;
|
|
ti->m_nRefCounter = 1;
|
|
ti->m_Flags &= ~FT_NOSTREAM;
|
|
ti->m_Flags2 |= FT2_WASLOADED;
|
|
ti->m_Id = i;
|
|
ti->m_Next = NULL;
|
|
ti->m_Prev = NULL;
|
|
AddToHash(ti->m_Bind, ti);
|
|
m_LastTex = ti;
|
|
return ti;
|
|
}*/
|
|
|
|
/*if (ti->m_bBusy && (ti->m_fAmount1 != fAmount1 || ti->m_fAmount2 != fAmount2) && !strstr(nameTex, "_ddn"))
|
|
{
|
|
iLog->Log("Warning: Changed amount in '%s' (Count: %d) from %.3f:%.3f to %.3f:%.3f", nameTex, ti->m_nRefCounter, ti->m_fAmount1, ti->m_fAmount2, fAmount1, fAmount2);
|
|
ti->m_fAmount1 = fAmount1;
|
|
ti->m_fAmount2 = fAmount2;
|
|
flags2 |= FT2_RELOAD;
|
|
}*/
|
|
m_LastTex = ti;
|
|
if (ti->m_bBusy && !(flags2 & FT2_RELOAD))
|
|
{
|
|
ti->m_Flags2 |= FT2_WASFOUND;
|
|
if (ti->m_eTT == eTT_Cubemap && ti->m_CubeSide == 0)
|
|
m_pCurCubeTexture = ti->m_RefTex.m_VidTex;
|
|
return ti;
|
|
}
|
|
|
|
ti->m_bBusy = true;
|
|
ti->m_LoadedSize = 0;
|
|
i = ti->m_Id;
|
|
|
|
ti->m_Flags = flags;
|
|
ti->m_Flags2 = flags2 & ~(FT2_RELOAD | FT2_WASFOUND);
|
|
if (!bind)
|
|
ti->m_Bind = TX_FIRSTBIND + i;
|
|
else
|
|
ti->m_Bind = bind;
|
|
|
|
if (m_Streamed & 1)
|
|
{
|
|
if (LoadFromCache(ti, flags, flags2, NULL, NULL, (ETexType)eTT))
|
|
return ti;
|
|
}
|
|
|
|
strcpy(nametex, ti->m_Name.c_str());
|
|
|
|
char name[256];
|
|
|
|
strcpy(name, *ti->m_SearchName);
|
|
STexLoaded *tl = ti->m_TL;
|
|
STexPic *tmp;
|
|
if (name[0] != '$')
|
|
{
|
|
tmp = LoadFromImage(name, ti->m_Flags, ti->m_Flags2, eTT, bind, ti, fAmount1, fAmount2);
|
|
if (tmp)
|
|
tmp->m_Flags2 |= FT2_WASLOADED;
|
|
}
|
|
else
|
|
{
|
|
tmp = ti;
|
|
AddToHash(ti->m_Bind, ti);
|
|
ti->m_RefTex.m_VidTex = NULL;
|
|
ti->m_ETF = eTF_8888;
|
|
}
|
|
if (tmp)
|
|
{
|
|
m_LastTex = tmp;
|
|
if (CRenderer::CV_r_logusedtextures)
|
|
{
|
|
sLogTexture(*ti->m_SearchName, ti->m_Size);
|
|
}
|
|
return tmp;
|
|
}
|
|
if (!i)
|
|
{
|
|
ti->m_bBusy = true;
|
|
ti->m_nRefCounter = 1;
|
|
ti->m_Flags |= FT_NOTFOUND;
|
|
ti->m_Id = i;
|
|
return ti;
|
|
}
|
|
int Tgt = ti->m_TargetType;
|
|
if (m_Textures[0])
|
|
*ti = *(m_Textures[0]);
|
|
else
|
|
iConsole->Exit("Couldn't find default texture '%s\n", nameTex);
|
|
ti->m_eTT = (ETexType)eTT;
|
|
ti->m_TL = tl;
|
|
ti->m_Name = nametex;
|
|
ti->m_SearchName = name;
|
|
ti->m_SourceName = name;
|
|
ti->m_TargetType = Tgt;
|
|
ti->m_nRefCounter = 1;
|
|
if (i)
|
|
ti->m_Flags &= ~(FT_NOREMOVE | FT_NORESIZE);
|
|
ti->m_Flags |= FT_NOTFOUND;
|
|
ti->m_Flags |= flags;
|
|
//ti->m_Flags &= ~FT_NOSTREAM;
|
|
ti->m_Flags2 &= ~FT2_WASLOADED;
|
|
ti->m_Size = 0;
|
|
ti->m_Id = i;
|
|
ti->m_Next = NULL;
|
|
ti->m_Prev = NULL;
|
|
if (bind)
|
|
{
|
|
ti->m_eTT = (ETexType)eTT;
|
|
ti->m_Bind = bind;
|
|
AddToHash(ti->m_Bind, ti);
|
|
ti->m_RefTex.m_VidTex = NULL;
|
|
}
|
|
m_LastTex = ti;
|
|
|
|
return ti;
|
|
}
|
|
|
|
STexPic *CTexMan::UploadImage(CImageFile* im, const char *name, uint flags, uint flags2, byte eTT, int bind, STexPic *ti, float fAmount1, float fAmount2)
|
|
{
|
|
byte *dst;
|
|
int i, m;
|
|
byte pal[256][4];
|
|
dst = NULL;
|
|
int DXTSize = 0;
|
|
byte *pix;
|
|
|
|
ti->m_bBusy = true;
|
|
if (!bind)
|
|
ti->m_Bind = TX_FIRSTBIND + ti->m_Id;
|
|
else
|
|
ti->m_Bind = bind;
|
|
AddToHash(ti->m_Bind, ti);
|
|
|
|
ETEX_Format eTF = eTF_8888;
|
|
ETEX_Format txf = eTF_Unknown;
|
|
|
|
ti->m_Width = im->mfGet_width();
|
|
ti->m_Height = im->mfGet_height();
|
|
ti->m_Depth = im->mfGet_depth();
|
|
ti->m_nMips = im->mfGet_numMips();
|
|
|
|
ti->m_Flags = flags;
|
|
ti->m_Flags2 = flags2;
|
|
if (im->mfGet_Flags() & FIM_NORMALMAP)
|
|
ti->m_Flags |= FT_HASNORMALMAP | FT_HASMIPS;
|
|
else
|
|
if (im->mfGet_Flags() & FIM_DSDT)
|
|
ti->m_Flags |= FT_HASDSDT | FT_HASMIPS;
|
|
if (ti->m_nMips > 1)
|
|
ti->m_Flags |= FT_HASMIPS;
|
|
else
|
|
if (ti->m_nMips == 1)
|
|
ti->m_Flags |= FT_NOMIPS;
|
|
|
|
switch (im->mfGetFormat())
|
|
{
|
|
case eIF_Gif:
|
|
case eIF_Pcx:
|
|
txf = eTF_Index;
|
|
break;
|
|
|
|
case eIF_Bmp:
|
|
if (im->mfGet_bps() == 32)
|
|
txf = eTF_8888;
|
|
else
|
|
if (im->mfGet_bps() == 24)
|
|
txf = eTF_0888;
|
|
else
|
|
if (im->mfGet_bps() == 8)
|
|
txf = eTF_Index;
|
|
break;
|
|
|
|
case eIF_Jpg:
|
|
case eIF_Tga:
|
|
if (im->mfGet_bps() == 32)
|
|
txf = eTF_8888;
|
|
else
|
|
txf = eTF_0888;
|
|
break;
|
|
|
|
case eIF_DXT1:
|
|
txf = eTF_DXT1;
|
|
break;
|
|
|
|
case eIF_DXT3:
|
|
txf = eTF_DXT3;
|
|
break;
|
|
|
|
case eIF_DXT5:
|
|
txf = eTF_DXT5;
|
|
break;
|
|
|
|
case eIF_DDS_RGB8:
|
|
txf = eTF_8888;
|
|
break;
|
|
|
|
case eIF_DDS_RGBA8:
|
|
txf = eTF_8888;
|
|
break;
|
|
|
|
case eIF_DDS_RGBA4:
|
|
txf = eTF_4444;
|
|
ti->m_Flags |= FT_HASALPHA;
|
|
break;
|
|
|
|
case eIF_DDS_DSDT:
|
|
if (im->mfGet_Flags() & FIM_DSDT)
|
|
txf = eTF_DSDT_MAG;
|
|
else
|
|
assert(0);
|
|
break;
|
|
|
|
case eIF_DDS_LUMINANCE:
|
|
txf = eTF_8000;
|
|
break;
|
|
|
|
case eIF_DDS_SIGNED_RGB8:
|
|
if (im->mfGet_Flags() & FIM_NORMALMAP)
|
|
txf = eTF_SIGNED_RGB8;
|
|
else
|
|
assert(0);
|
|
break;
|
|
|
|
case eIF_DDS_SIGNED_HILO8:
|
|
if (im->mfGet_Flags() & FIM_NORMALMAP)
|
|
txf = eTF_SIGNED_HILO8;
|
|
else
|
|
assert(0);
|
|
break;
|
|
|
|
case eIF_DDS_SIGNED_HILO16:
|
|
if (im->mfGet_Flags() & FIM_NORMALMAP)
|
|
txf = eTF_SIGNED_HILO16;
|
|
else
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
if (txf == -1)
|
|
return NULL;
|
|
|
|
ti->m_ETF = txf;
|
|
pix = im->mfGet_image();
|
|
|
|
switch(txf)
|
|
{
|
|
case eTF_Index:
|
|
{
|
|
SRGBPixel *pPal = im->m_pPal;
|
|
for (i=0; i<256; i++)
|
|
{
|
|
pal[i][0] = pPal[i].red;
|
|
pal[i][1] = pPal[i].green;
|
|
pal[i][2] = pPal[i].blue;
|
|
pal[i][3] = pPal[i].alpha;
|
|
}
|
|
|
|
if (ti->m_Flags & FT_DYNAMIC)
|
|
{
|
|
ti->m_pPalette = new SRGBPixel[256];
|
|
byte a = 255;
|
|
if (m_bRGBA)
|
|
{
|
|
for (m=0; m<256; m++)
|
|
{
|
|
*(uint *)(&ti->m_pPalette[m]) = *(uint *)(&pal[m][0]);
|
|
a &= pal[m][3];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (m=0; m<256; m++)
|
|
{
|
|
ti->m_pPalette[m].red = pal[m][2];
|
|
ti->m_pPalette[m].green = pal[m][1];
|
|
ti->m_pPalette[m].blue = pal[m][0];
|
|
ti->m_pPalette[m].alpha = pal[m][3];
|
|
a &= pal[m][3];
|
|
}
|
|
}
|
|
if (a != 255)
|
|
ti->m_Flags |= FT_HASALPHA;
|
|
ti->m_pData = new byte[ti->m_Width*ti->m_Height];
|
|
cryMemcpy(ti->m_pData, pix, ti->m_Width*ti->m_Height);
|
|
ti->m_Flags |= FT_PALETTED;
|
|
}
|
|
dst = new byte [ti->m_Width*ti->m_Height*4];
|
|
FillBGRA_8to32(dst, pix, pal, ti->m_Width, ti->m_Height, ti);
|
|
}
|
|
break;
|
|
|
|
case eTF_0888:
|
|
{
|
|
if (ti->m_Flags & FT_HASMIPS)
|
|
{
|
|
int Size = im->mfGet_ImageSize();
|
|
dst = new byte [Size/3*4];
|
|
FillBGRA_24to32(dst, pix, Size);
|
|
}
|
|
else
|
|
dst = pix;
|
|
if (ti->m_Flags & FT_DYNAMIC)
|
|
{
|
|
int i = 256;
|
|
ti->m_pPalette = new SRGBPixel[i];
|
|
ti->m_pData = new byte[ti->m_Width*ti->m_Height];
|
|
shQuantizeRGB((SRGBPixel *)dst, ti->m_Width*ti->m_Height, ti->m_Width, ti->m_pData, ti->m_pPalette, i, CRenderer::CV_r_texquantizedither ? 1 : 0);
|
|
eTF = eTF_Index;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eTF_8888:
|
|
{
|
|
dst = pix;
|
|
byte aMin = 255;
|
|
byte aMax = 0;
|
|
for (i=0; i<ti->m_Width*ti->m_Height; i++)
|
|
{
|
|
byte a = dst[i*4+3];
|
|
aMin = min(a, aMin);
|
|
aMax = max(a, aMax);
|
|
}
|
|
if (aMax != aMin)
|
|
ti->m_Flags |= FT_HASALPHA;
|
|
if (ti->m_Flags & FT_DYNAMIC)
|
|
{
|
|
int i = 256;
|
|
ti->m_pPalette = new SRGBPixel[i];
|
|
ti->m_pData = new byte[ti->m_Width*ti->m_Height];
|
|
shQuantizeRGB((SRGBPixel *)dst, ti->m_Width*ti->m_Height, ti->m_Width, ti->m_pData, ti->m_pPalette, i, CRenderer::CV_r_texquantizedither ? 1 : 0);
|
|
eTF = eTF_Index;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eTF_4444:
|
|
{
|
|
ti->m_Flags |= FT_HASALPHA;
|
|
dst = pix;
|
|
eTF = eTF_4444;
|
|
}
|
|
break;
|
|
|
|
case eTF_RGB8:
|
|
case eTF_SIGNED_RGB8:
|
|
case eTF_SIGNED_HILO8:
|
|
case eTF_SIGNED_HILO16:
|
|
{
|
|
dst = pix;
|
|
if (im->mfGet_numMips() == 1)
|
|
ti->m_Flags |= FT_NOMIPS;
|
|
eTF = eTF_0888;
|
|
}
|
|
break;
|
|
|
|
case eTF_DSDT_MAG:
|
|
ti->m_eTT = eTT_DSDTBump;
|
|
eTF = eTF_DSDT_MAG;
|
|
dst = pix;
|
|
break;
|
|
case eTF_8000:
|
|
dst = pix;
|
|
eTF = eTF_8000;
|
|
break;
|
|
case eTF_DXT1:
|
|
DXTSize = im->mfGet_ImageSize();
|
|
dst = pix;
|
|
ti->m_Flags |= FT_DXT1;
|
|
eTF = eTF_DXT1;
|
|
break;
|
|
case eTF_DXT3:
|
|
DXTSize = im->mfGet_ImageSize();
|
|
dst = pix;
|
|
ti->m_Flags |= FT_DXT3;
|
|
eTF = eTF_DXT3;
|
|
break;
|
|
case eTF_DXT5:
|
|
DXTSize = im->mfGet_ImageSize();
|
|
dst = pix;
|
|
ti->m_Flags |= FT_DXT5;
|
|
eTF = eTF_DXT5;
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
TArray<byte> Dst;
|
|
#ifdef USE_3DC
|
|
if (txf == eTF_0888 || txf == eTF_8888)
|
|
{
|
|
#ifndef NULL_RENDERER
|
|
if (eTT == eTT_Bumpmap && (ti->m_Flags & FT_3DC) && gRenDev->m_bDeviceSupportsComprNormalmaps)
|
|
{
|
|
int i, nMip;
|
|
int wdt = ti->m_Width;
|
|
int hgt = ti->m_Height;
|
|
bool bAllow = true;
|
|
if (gRenDev->m_bDeviceSupportsComprNormalmaps == 1)
|
|
{
|
|
if (CRenderer::CV_r_texnormalmapcompressed == 1 && wdt != hgt)
|
|
bAllow = false;
|
|
if (!CompressTextureATI)
|
|
bAllow = false;
|
|
}
|
|
if (bAllow)
|
|
{
|
|
int Offs = 0;
|
|
for (nMip=0; nMip<ti->m_nMips; nMip++)
|
|
{
|
|
if (wdt <= 0)
|
|
wdt = 1;
|
|
if (hgt <= 0)
|
|
hgt = 1;
|
|
byte *d = &dst[Offs];
|
|
if (gRenDev->m_bDeviceSupportsComprNormalmaps == 1)
|
|
{
|
|
if (!(ti->m_Flags & FT_3DC_A))
|
|
{
|
|
for (i=0; i<wdt*hgt; i++)
|
|
{
|
|
Exchange(d[i*4+1], d[i*4+2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<wdt*hgt; i++)
|
|
{
|
|
d[i*4+1] = d[i*4+3];
|
|
d[i*4+2] = d[i*4+0];
|
|
d[i*4+3] = 255;
|
|
d[i*4+0] = 255;
|
|
}
|
|
}
|
|
void *outData = NULL;
|
|
DWORD outSize = 0;
|
|
COMPRESSOR_ERROR err = CompressTextureATI(wdt, hgt, FORMAT_ARGB_8888, FORMAT_COMP_ATI2N, d, &outData, &outSize);
|
|
if (err == COMPRESSOR_ERROR_NONE)
|
|
{
|
|
int nDst = Dst.Num();
|
|
Dst.Grow(outSize);
|
|
memcpy(&Dst[nDst], outData, outSize);
|
|
DeleteDataATI(outData);
|
|
}
|
|
else
|
|
{
|
|
ti->m_Flags &= ~FT_3DC;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
if (gRenDev->m_bDeviceSupportsComprNormalmaps > 1)
|
|
{
|
|
int nDst = Dst.Num();
|
|
Dst.Grow(wdt*hgt*2);
|
|
byte *dst = &Dst[nDst];
|
|
if (!(ti->m_Flags & FT_3DC_A))
|
|
{
|
|
for (i=0; i<wdt*hgt; i++)
|
|
{
|
|
dst[i*2+1] = d[i*4+1] - 128;
|
|
dst[i*2+0] = d[i*4+2] - 128;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<wdt*hgt; i++)
|
|
{
|
|
dst[i*2+0] = d[i*4+3] - 128;
|
|
dst[i*2+1] = 0;
|
|
}
|
|
}
|
|
}
|
|
Offs += wdt * hgt * 4;
|
|
wdt >>= 1;
|
|
hgt >>= 1;
|
|
}
|
|
if (Dst.Num())
|
|
{
|
|
dst = &Dst[0];
|
|
}
|
|
}
|
|
else
|
|
ti->m_Flags &= ~FT_3DC;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
CreateTexture(NULL, ti->m_Width, ti->m_Height, ti->m_Depth, ti->m_Flags, ti->m_Flags2, dst, ti->m_eTT, fAmount1, fAmount2, DXTSize, ti, ti->m_Bind, eTF);
|
|
//ti->Release(false);
|
|
|
|
if (dst && ti->m_pData32 != dst && dst != pix && dst != Dst.Data())
|
|
delete [] dst;
|
|
|
|
return ti;
|
|
}
|
|
|
|
ETEX_Format sImageFormat2TexFormat(EImFormat eImF)
|
|
{
|
|
switch(eImF)
|
|
{
|
|
case eIF_DDS_RGBA8:
|
|
case eIF_Jpg:
|
|
case eIF_Tga:
|
|
case eIF_Bmp:
|
|
return eTF_8888;
|
|
case eIF_DDS_RGB8:
|
|
return eTF_0888;
|
|
case eIF_DXT1:
|
|
return eTF_DXT1;
|
|
case eIF_DXT3:
|
|
return eTF_DXT3;
|
|
case eIF_DXT5:
|
|
return eTF_DXT5;
|
|
default:
|
|
assert(0);
|
|
return eTF_Unknown;
|
|
}
|
|
|
|
return eTF_Unknown;
|
|
}
|
|
|
|
char *gImgExt[][16] =
|
|
{
|
|
{
|
|
".dds",
|
|
".tga",
|
|
".jpg",
|
|
NULL
|
|
},
|
|
{
|
|
".dds",
|
|
".tga",
|
|
".jpg",
|
|
NULL
|
|
},
|
|
{
|
|
".dds",
|
|
NULL
|
|
}
|
|
,
|
|
{
|
|
".pcx",
|
|
".tga",
|
|
".jpg",
|
|
NULL
|
|
},
|
|
};
|
|
|
|
|
|
STexPic *CTexMan::LoadFromImage (const char *name, uint flags, uint flags2, byte eTT, int bind, STexPic *ti, float fAmount1, float fAmount2)
|
|
{
|
|
char nm[2][256];
|
|
char nam[2][256];
|
|
CImageFile* im[2];
|
|
STexPic *tx = NULL;
|
|
char wasloaded[2][256];
|
|
int i;
|
|
|
|
wasloaded[0][0] = 0;
|
|
wasloaded[1][0] = 0;
|
|
im[0] = im[1] = NULL;
|
|
nam[0][0] = nam[1][0] = 0;
|
|
|
|
strcpy(nm[0], name);
|
|
strlwr(nm[0]);
|
|
|
|
ConvertDOSToUnixName(nm[0], nm[0]);
|
|
|
|
int nI;
|
|
if (flags & FT_DYNAMIC)
|
|
nI = 3;
|
|
else
|
|
if ((eTT == eTT_Base || eTT == eTT_Cubemap) && (flags2 & FT2_NODXT))
|
|
nI = 1;
|
|
else
|
|
nI = 0;
|
|
char *str;
|
|
int nT = 1;
|
|
char suffix[128];
|
|
if (str=strchr(nm[0], '+'))
|
|
{
|
|
nT++;
|
|
*str = 0;
|
|
str++;
|
|
suffix[0] = '+';
|
|
int ns = 1;
|
|
while (*str != '_')
|
|
{
|
|
suffix[ns++] = *str;
|
|
str++;
|
|
}
|
|
suffix[ns] = '_';
|
|
suffix[ns+1] = 0;
|
|
strcpy(nm[1], &str[1]);
|
|
StripExtension(nm[1], nm[1]);
|
|
StripExtension(nm[0], nm[0]);
|
|
}
|
|
for (i=0; i<nT; i++)
|
|
{
|
|
for (int n=0; gImgExt[nI][n]; n++)
|
|
{
|
|
strcpy(nam[i], nm[i]);
|
|
strcat(nam[i], gImgExt[nI][n]);
|
|
|
|
im[i] = NULL;
|
|
im[i] = CImageFile::mfLoad_file(nam[i]);
|
|
if ( !im[i] )
|
|
continue;
|
|
if (n)
|
|
Warning(VALIDATOR_FLAG_TEXTURE, nam[i], "Texture format '%s' is deprecated ('%s') (use .dds instead)", gImgExt[nI][n], nam[i]);
|
|
if (im[i]->mfGet_error() != eIFE_OK || im[i]->mfGetFormat() == eIF_Unknown)
|
|
{
|
|
delete im[i];
|
|
continue;
|
|
}
|
|
if ((im[i]->mfGet_Flags() & FIM_NORMALMAP) && eTT != eTT_Bumpmap)
|
|
{
|
|
delete im[i];
|
|
continue;
|
|
}
|
|
if ((im[i]->mfGet_Flags() & FIM_DSDT) && eTT != eTT_DSDTBump)
|
|
{
|
|
delete im[i];
|
|
continue;
|
|
}
|
|
if (im[i]->mfGetFormat() == eIF_DDS_RGB8 || im[i]->mfGetFormat() == eIF_DDS_DSDT)
|
|
{
|
|
im[i]->m_eFormat = eIF_DDS_RGBA8;
|
|
}
|
|
|
|
|
|
bool bComp = ((gRenDev->GetFeatures() & RFT_COMPRESSTEXTURE) != 0);
|
|
if ((!bComp && !(flags2 & FT2_FORCEDXT)) || eTT == eTT_Bumpmap || eTT == eTT_DSDTBump)
|
|
{
|
|
if (!(im[i]->mfGet_Flags() & (FIM_NORMALMAP | FIM_DSDT)) && (im[i]->mfGetFormat() == eIF_DXT1 || im[i]->mfGetFormat() == eIF_DXT3 || im[i]->mfGetFormat() == eIF_DXT5))
|
|
{
|
|
strcpy(wasloaded[i], nam[i]);
|
|
delete im[i];
|
|
|
|
//Warning( 0,0,"Warning: CTexMan::LoadFromImage: Texture skipped because it's compressed: %s [Ext=%s]", name, gImgExt[nI][n]);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
wasloaded[i][0] = 0;
|
|
break;
|
|
}
|
|
}
|
|
for (i=0; i<nT; i++)
|
|
{
|
|
if (wasloaded[i][0])
|
|
{
|
|
#if !defined(NULL_RENDERER)
|
|
#ifdef WIN64
|
|
Warning( VALIDATOR_FLAG_TEXTURE,wasloaded[i],"Error: CTexMan::LoadFromImage: Generate normal map from compressed texture: '%s', "
|
|
"Loading of compressed bump-maps is not supported. "
|
|
"If you want the engine to compress it please use _ddn_cct postfix in texture name",
|
|
wasloaded[i]);
|
|
return LoadTexture("Textures/white_ddn", 0,0);
|
|
#endif
|
|
#endif
|
|
STexPic tp;
|
|
im[i] = CImageFile::mfLoad_file(wasloaded[i]);
|
|
if ( !im[i] )
|
|
return NULL;
|
|
strcpy(nam[i], wasloaded[i]);
|
|
tp.m_Width = im[i]->mfGet_width();
|
|
tp.m_Height = im[i]->mfGet_height();
|
|
tp.m_Flags = 0;
|
|
switch (im[i]->mfGetFormat())
|
|
{
|
|
case eIF_DXT1:
|
|
tp.m_Flags |= FT_DXT1;
|
|
break;
|
|
case eIF_DXT3:
|
|
tp.m_Flags |= FT_DXT3;
|
|
break;
|
|
case eIF_DXT5:
|
|
tp.m_Flags |= FT_DXT5;
|
|
break;
|
|
}
|
|
#if !defined(NULL_RENDERER)
|
|
Warning( VALIDATOR_FLAG_TEXTURE,wasloaded[i],"Error: CTexMan::LoadFromImage: Generate normal map from compressed texture: '%s', "
|
|
"Loading of compressed bump-maps is not supported. "
|
|
"If you want the engine to compress it please use _ddn_cct postfix in texture name",
|
|
wasloaded[i]);
|
|
#endif
|
|
byte *data_ = ImgConvertDXT_RGBA(im[i]->mfGet_image(), &tp, im[i]->mfGet_ImageSize());
|
|
SAFE_DELETE_ARRAY (im[i]->m_pByteImage);
|
|
|
|
//Watch out this is a BIG problem on PS2
|
|
//During new/delete the data MUST be the same.
|
|
//That's the case where mspImage "is casted" from byte to
|
|
//SRGBPixel causing a memory bug on PS2
|
|
//This is a workaround... maybe there's a better elegant solution
|
|
SRGBPixel *pNewImage = new SRGBPixel[tp.m_Width*tp.m_Height];
|
|
for(int j=0; j<tp.m_Width*tp.m_Height; j++)
|
|
{
|
|
pNewImage[j].red=data_[j*4+0];
|
|
pNewImage[j].green=data_[j*4+1];
|
|
pNewImage[j].blue=data_[j*4+2];
|
|
pNewImage[j].alpha=data_[j*4+3];
|
|
}
|
|
im[i]->m_pPixImage = pNewImage;
|
|
|
|
delete [] data_;
|
|
|
|
im[i]->m_eFormat = eIF_Tga;
|
|
im[i]->mfSet_ImageSize(tp.m_Width*tp.m_Height*4);
|
|
im[i]->mfSet_numMips(1);
|
|
im[i]->m_Bps = 32;
|
|
}
|
|
}
|
|
if (im[0] && im[1])
|
|
{
|
|
char str[256];
|
|
sprintf(str, "%s%s%s", nam[0], suffix, nam[1]);
|
|
ti->m_SourceName = str;
|
|
}
|
|
else
|
|
if (im[0])
|
|
ti->m_SourceName = nam[0];
|
|
else
|
|
if (im[1])
|
|
{
|
|
ti->m_SourceName = nam[1];
|
|
im[0] = im[1];
|
|
im[1] = NULL;
|
|
}
|
|
|
|
if (im[0] && (eTT == eTT_Bumpmap || eTT == eTT_DSDTBump))
|
|
{
|
|
flags &= ~FT_NOMIPS;
|
|
GenerateNormalMap(im, flags, flags2, eTT, fAmount1, fAmount2, ti);
|
|
}
|
|
|
|
if (!(flags & FT_DYNAMIC))
|
|
ImagePreprocessing(im[0], flags, flags2, eTT, ti);
|
|
|
|
#ifndef NULL_RENDERER
|
|
if (im[0] && !ti->m_pSH && eTT == eTT_Bumpmap && im[0]->mfGetFormat()!=eIF_DDS_RGB8 && (strstr(ti->m_SourceName.c_str(), "_shadow") || CRenderer::CV_r_bumpselfshadow==2))
|
|
{
|
|
ti->m_pSH = new STexShadow;
|
|
ti->m_pSH->Init(im[0]->mfGet_image(), im[0]->mfGet_width(), im[0]->mfGet_height(), false);
|
|
}
|
|
#endif
|
|
|
|
if (im[0])
|
|
tx = UploadImage(im[0], name, flags, flags2, eTT, bind, ti, fAmount1, fAmount2);
|
|
delete im[0];
|
|
delete im[1];
|
|
if (!tx)
|
|
return NULL;
|
|
|
|
return tx;
|
|
}
|
|
|
|
//===============================================================================
|
|
|
|
void CTexMan::ImagePreprocessing(CImageFile* im, uint flags, uint flags2, byte eTT, STexPic *ti)
|
|
{
|
|
if (!im)
|
|
return;
|
|
EImFormat imf = im->m_eFormat;
|
|
int nMips = im->mfGet_numMips();
|
|
int width = im->mfGet_width();
|
|
int height = im->mfGet_height();
|
|
ti->m_WidthOriginal = width;
|
|
ti->m_HeightOriginal = height;
|
|
int nWidth = ilog2(width);
|
|
int nHeight = ilog2(height);
|
|
bool bPowerOfTwo = ((nWidth == width) && (nHeight == height));
|
|
int nComps;
|
|
switch (imf)
|
|
{
|
|
case eIF_DDS_RGB8:
|
|
case eIF_DDS_SIGNED_RGB8:
|
|
case eIF_DDS_DSDT:
|
|
nComps = 3;
|
|
break;
|
|
case eIF_DDS_RGBA8:
|
|
nComps = 4;
|
|
break;
|
|
case eIF_Gif:
|
|
case eIF_Pcx:
|
|
case eIF_DDS_LUMINANCE:
|
|
nComps = 1;
|
|
break;
|
|
case eIF_DDS_RGBA4:
|
|
nComps = 2;
|
|
break;
|
|
default:
|
|
nComps = 4;
|
|
}
|
|
if (!(m_Streamed & 1) && !(flags & FT_NORESIZE))
|
|
{
|
|
int minSize = max(CRenderer::CV_r_texminsize, 16);
|
|
if (eTT == eTT_Bumpmap)
|
|
{
|
|
if (CRenderer::CV_r_texbumpresolution > 0)
|
|
{
|
|
if (nWidth >= minSize || nHeight >= minSize)
|
|
{
|
|
int nRes = min(CRenderer::CV_r_texbumpresolution, 4);
|
|
nWidth = max(nWidth>>nRes, 1);
|
|
nHeight = max(nHeight>>nRes, 1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (flags & FT_SKY)
|
|
{
|
|
if (CRenderer::CV_r_texskyresolution > 0)
|
|
{
|
|
if (nWidth >= minSize || nHeight >= minSize)
|
|
{
|
|
int nRes = min(CRenderer::CV_r_texskyresolution, 4);
|
|
nWidth = max(nWidth>>nRes, 1);
|
|
nHeight = max(nHeight>>nRes, 1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (flags & FT_LM)
|
|
{
|
|
if (CRenderer::CV_r_texlmresolution > 0)
|
|
{
|
|
if (nWidth >= minSize || nHeight >= minSize)
|
|
{
|
|
int nRes = min(CRenderer::CV_r_texlmresolution, 4);
|
|
nWidth = max(nWidth>>nRes, 1);
|
|
nHeight = max(nHeight>>nRes, 1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CRenderer::CV_r_texresolution > 0)
|
|
{
|
|
// Do no resize TGA textures and DSDT textures
|
|
if (ti->m_eTT != eTT_DSDTBump && im->m_eFormat != eIF_Tga)
|
|
{
|
|
if (nWidth >= minSize || nHeight >= minSize)
|
|
{
|
|
int nRes = min(CRenderer::CV_r_texresolution, 4);
|
|
nWidth = max(nWidth>>nRes, 1);
|
|
nHeight = max(nHeight>>nRes, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nWidth != nHeight && CRenderer::CV_r_texforcesquare)
|
|
{
|
|
nWidth = max(nWidth, nHeight);
|
|
nHeight = nWidth;
|
|
}
|
|
// Hardware limitation check
|
|
if (gRenDev->m_MaxTextureSize)
|
|
{
|
|
if (nWidth > gRenDev->m_MaxTextureSize)
|
|
nWidth = gRenDev->m_MaxTextureSize;
|
|
if (nHeight > gRenDev->m_MaxTextureSize)
|
|
nHeight = gRenDev->m_MaxTextureSize;
|
|
}
|
|
if (nWidth <= 0)
|
|
nWidth = 1;
|
|
if (nHeight <= 0)
|
|
nHeight = 1;
|
|
// User limitation check
|
|
if (CRenderer::CV_r_texmaxsize > 1 && im->m_eFormat != eIF_Tga)
|
|
{
|
|
CRenderer::CV_r_texmaxsize = ilog2(CRenderer::CV_r_texmaxsize);
|
|
|
|
if (nWidth > CRenderer::CV_r_texmaxsize)
|
|
nWidth = CRenderer::CV_r_texmaxsize;
|
|
if (nHeight > CRenderer::CV_r_texmaxsize)
|
|
nHeight = CRenderer::CV_r_texmaxsize;
|
|
}
|
|
}
|
|
|
|
if (!bPowerOfTwo)
|
|
{
|
|
if (imf == eIF_DXT1 || imf == eIF_DXT3 || imf == eIF_DXT5)
|
|
Warning( VALIDATOR_FLAG_TEXTURE,im->m_FileName,"Error: CTexMan::ImagePreprocessing: Attempt to load of non-power-of-two compressed texture '%s' (%dx%d)", im->m_FileName, width, height);
|
|
else
|
|
Warning( VALIDATOR_FLAG_TEXTURE,im->m_FileName,"Warning: CTexMan::ImagePreprocessing: Attempt to load of non-power-of-two texture '%s' (%dx%d)", im->m_FileName, width, height);
|
|
}
|
|
else
|
|
if ((flags & FT_NORESIZE))
|
|
return;
|
|
if (nWidth != width || nHeight != height)
|
|
{
|
|
if (imf == eIF_DXT1 || imf == eIF_DXT3 || imf == eIF_DXT5)
|
|
{
|
|
bool bResample = false;
|
|
if (bPowerOfTwo)
|
|
{
|
|
if (nMips > 1)
|
|
{
|
|
int nLodDW = 0;
|
|
int nLodDH = 0;
|
|
int nOffs = 0;
|
|
int blockSize = imf == eIF_DXT1 ? 8 : 16;
|
|
int wdt = width;
|
|
int hgt = height;
|
|
int n = 0;
|
|
while (wdt || hgt)
|
|
{
|
|
if (!wdt)
|
|
wdt = 1;
|
|
if (!hgt)
|
|
hgt = 1;
|
|
if (wdt == nWidth)
|
|
nLodDW = n;
|
|
if (hgt == nHeight)
|
|
nLodDH = n;
|
|
if (nLodDH && nLodDW)
|
|
break;
|
|
nOffs += ((wdt+3)/4)*((hgt+3)/4)*blockSize;
|
|
wdt >>= 1;
|
|
hgt >>= 1;
|
|
n++;
|
|
}
|
|
if (nLodDH != nLodDW)
|
|
{
|
|
Warning( VALIDATOR_FLAG_TEXTURE,im->m_FileName,"Error: CTexMan::ImagePreprocessing: Scaling of '%s' compressed texture is dangerous (non-proportional scaling)", im->m_FileName);
|
|
}
|
|
else
|
|
if (n)
|
|
{
|
|
byte *dst = im->m_pByteImage;
|
|
int nSize = im->mfGet_ImageSize();
|
|
memmove(dst, &dst[nOffs], nSize-nOffs);
|
|
im->mfSet_ImageSize(nSize-nOffs);
|
|
im->mfSet_numMips(nMips-n);
|
|
im->mfSet_dimensions(nWidth, nHeight);
|
|
bResample = true;
|
|
}
|
|
}
|
|
else
|
|
Warning( VALIDATOR_FLAG_TEXTURE,im->m_FileName,"Error: CTexMan::ImagePreprocessing: Scaling of '%s' compressed texture is dangerous (only one mip)", im->m_FileName);
|
|
}
|
|
#ifndef WIN64
|
|
if (!bResample)
|
|
{
|
|
switch (imf)
|
|
{
|
|
case eIF_DXT1:
|
|
ti->m_Flags |= FT_DXT1;
|
|
break;
|
|
case eIF_DXT3:
|
|
ti->m_Flags |= FT_DXT3;
|
|
break;
|
|
case eIF_DXT5:
|
|
ti->m_Flags |= FT_DXT5;
|
|
break;
|
|
}
|
|
ti->m_Width = width;
|
|
ti->m_Height = height;
|
|
byte *data_ = ImgConvertDXT_RGBA(im->mfGet_image(), ti, im->mfGet_ImageSize());
|
|
//::WriteTGA(data_, width, height, "bug.tga", 32);
|
|
byte *dst = new byte[nWidth * nHeight * 4];
|
|
byte *src = data_;
|
|
ImgResample((uint *)dst, nWidth, nHeight, (uint *)src, width, height);
|
|
//::WriteTGA(dst, nWidth, nHeight, "bug1.tga", 32);
|
|
SAFE_DELETE_ARRAY(im->m_pByteImage);
|
|
SAFE_DELETE_ARRAY(data_);
|
|
ti->m_Width = nWidth;
|
|
ti->m_Height = nHeight;
|
|
bool bUseMips = true;
|
|
if (nMips > 1)
|
|
{
|
|
nMips = 0;
|
|
bUseMips = false;
|
|
}
|
|
int DXTSize = 0;
|
|
byte *dstDXT = ImgConvertRGBA_DXT(dst, ti, DXTSize, nMips, 32, bUseMips);
|
|
//WriteDDS(dstDXT, nWidth, nHeight, DXTSize, "bugDXT.dds", eIF_DXT1, nMips);
|
|
int wdt = nWidth;
|
|
int hgt = nHeight;
|
|
int n = 0;
|
|
int blockSize = imf == eIF_DXT1 ? 8 : 16;
|
|
int nSize = 0;
|
|
while (wdt || hgt)
|
|
{
|
|
if (!wdt)
|
|
wdt = 1;
|
|
if (!hgt)
|
|
hgt = 1;
|
|
nSize += ((wdt+3)/4)*((hgt+3)/4)*blockSize;
|
|
wdt >>= 1;
|
|
hgt >>= 1;
|
|
n++;
|
|
}
|
|
SAFE_DELETE_ARRAY(dst);
|
|
im->m_pByteImage = dstDXT;
|
|
im->mfSet_ImageSize(DXTSize);
|
|
im->mfSet_numMips(nMips);
|
|
im->mfSet_dimensions(nWidth, nHeight);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (bPowerOfTwo)
|
|
{
|
|
bool bResampled = false;
|
|
if (nMips > 1)
|
|
{
|
|
int nLodDW = 0;
|
|
int nLodDH = 0;
|
|
int nOffs = 0;
|
|
int wdt = width;
|
|
int hgt = height;
|
|
int n = 0;
|
|
while (wdt || hgt)
|
|
{
|
|
if (!wdt)
|
|
wdt = 1;
|
|
if (!hgt)
|
|
hgt = 1;
|
|
|
|
if (wdt == nWidth)
|
|
nLodDW = n;
|
|
if (hgt == nHeight)
|
|
nLodDH = n;
|
|
|
|
if (nLodDH && nLodDW)
|
|
break;
|
|
nOffs += wdt*hgt*nComps;
|
|
wdt >>= 1;
|
|
hgt >>= 1;
|
|
n++;
|
|
}
|
|
if (nLodDH == nLodDW && n)
|
|
{
|
|
byte *dst = im->m_pByteImage;
|
|
int nSize = im->mfGet_ImageSize();
|
|
memmove(dst, &dst[nOffs], nSize-nOffs);
|
|
im->mfSet_ImageSize(nSize-nOffs);
|
|
im->mfSet_numMips(nMips-n);
|
|
im->mfSet_dimensions(nWidth, nHeight);
|
|
bResampled = true;
|
|
}
|
|
}
|
|
if (!bResampled)
|
|
{
|
|
if (nComps == 1)
|
|
{
|
|
byte *dst = new byte[nWidth * nHeight];
|
|
byte *src = (byte *)im->m_pByteImage;
|
|
ImgResample8(dst, nWidth, nHeight, src, width, height);
|
|
SAFE_DELETE_ARRAY(im->m_pByteImage);
|
|
im->m_pByteImage = dst;
|
|
im->mfSet_ImageSize(nWidth*nHeight);
|
|
im->mfSet_numMips(0);
|
|
im->mfSet_dimensions(nWidth, nHeight);
|
|
}
|
|
else
|
|
{
|
|
byte *dst = new byte[nWidth * nHeight * 4];
|
|
byte *src = im->m_pByteImage;
|
|
ImgResample((uint *)dst, nWidth, nHeight, (uint *)src, width, height);
|
|
SAFE_DELETE_ARRAY(im->m_pByteImage);
|
|
im->m_pByteImage = dst;
|
|
im->mfSet_ImageSize(nWidth*nHeight*4);
|
|
im->mfSet_numMips(0);
|
|
im->mfSet_dimensions(nWidth, nHeight);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (nMips > 1)
|
|
{
|
|
byte *dst = new byte[nWidth * nHeight * 4];
|
|
byte *src = im->m_pByteImage;
|
|
byte *src1 = NULL;
|
|
if (nComps == 3)
|
|
{
|
|
src1 = new byte[width * height * 4];
|
|
for (int i=0; i<width*height; i++)
|
|
{
|
|
src1[i*4+0] = src[i*3+0];
|
|
src1[i*4+1] = src[i*3+1];
|
|
src1[i*4+2] = src[i*3+2];
|
|
src1[i*4+3] = 255;
|
|
}
|
|
src = src1;
|
|
}
|
|
ImgResample((uint *)dst, nWidth, nHeight, (uint *)src, width, height);
|
|
SAFE_DELETE_ARRAY(src1);
|
|
SAFE_DELETE_ARRAY(im->m_pByteImage);
|
|
im->m_pByteImage = dst;
|
|
im->mfSet_ImageSize(nWidth*nHeight*4);
|
|
im->mfSet_numMips(0);
|
|
im->mfSet_dimensions(nWidth, nHeight);
|
|
}
|
|
else
|
|
{
|
|
byte *dst = new byte[nWidth * nHeight * 4];
|
|
byte *src = im->m_pByteImage;
|
|
ImgResample((uint *)dst, nWidth, nHeight, (uint *)src, width, height);
|
|
SAFE_DELETE_ARRAY(im->m_pByteImage);
|
|
im->m_pByteImage = dst;
|
|
im->mfSet_ImageSize(nWidth*nHeight*4);
|
|
im->mfSet_numMips(0);
|
|
im->mfSet_dimensions(nWidth, nHeight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//===============================================================================
|
|
|
|
byte *CTexMan::ConvertRGB_Gray(byte *src, STexPic *ti, int flags, ETEX_Format eTF)
|
|
{
|
|
byte *dst = new byte [ti->m_Width*ti->m_Height];
|
|
int size = ti->m_Width*ti->m_Height;
|
|
int nComps = (eTF == eTF_0888) ? 3 : 4;
|
|
byte *src1 = src;
|
|
byte *dst1 = dst;
|
|
if (flags & FT_BUMP_DETRED)
|
|
while (size)
|
|
{
|
|
*dst1 = src1[0];
|
|
dst1++;
|
|
src1 += 4;
|
|
size--;
|
|
}
|
|
else
|
|
if (flags & FT_BUMP_DETBLUE)
|
|
while (size)
|
|
{
|
|
*dst1 = src1[2];
|
|
dst1++;
|
|
src1 += 4;
|
|
size--;
|
|
}
|
|
else
|
|
if (flags & FT_BUMP_DETALPHA)
|
|
while (size)
|
|
{
|
|
*dst1 = src1[3];
|
|
dst1++;
|
|
src1 += 4;
|
|
size--;
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_texgrayoverage)
|
|
{
|
|
while (size)
|
|
{
|
|
*dst1 = (src1[0] + src1[1] + src1[2]) / 3;
|
|
dst1++;
|
|
src1 += nComps;
|
|
size--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (size)
|
|
{
|
|
float fRed, fGreen, fBlue;
|
|
if (m_bRGBA)
|
|
{
|
|
fRed = src1[0] / 255.0f;
|
|
fGreen = src1[1] / 255.0f;
|
|
fBlue = src1[2] / 255.0f;
|
|
}
|
|
else
|
|
{
|
|
fRed = src1[2] / 255.0f;
|
|
fGreen = src1[1] / 255.0f;
|
|
fBlue = src1[0] / 255.0f;
|
|
}
|
|
float fLuminance = ((fRed * 0.3f) + (fGreen * 0.59f) + (fBlue * 0.11f));
|
|
*dst1 = (byte)(fLuminance * 255.0f);
|
|
dst1++;
|
|
src1 += nComps;
|
|
size--;
|
|
}
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
byte *CTexMan::GenerateNormalMap(byte *src, int width, int height, uint flags, uint flags2, byte eTT, float fAmount, STexPic *ti, int& nMips, int& nSize, ETEX_Format eTF)
|
|
{
|
|
byte *dst;
|
|
int i, j, l;
|
|
|
|
ti->m_Width = width;
|
|
ti->m_Height = height;
|
|
byte *gray = ConvertRGB_Gray(src, ti, flags, eTF);
|
|
|
|
/*if (!ti->m_pSH)
|
|
{
|
|
ti->m_pSH = new STexShadow;
|
|
ti->m_pSH->Init(gray, width, height, true);
|
|
}*/
|
|
|
|
bool bInv = (flags2 & FT2_BUMPINVERTED) ? true : false;
|
|
|
|
float fScale;
|
|
if (fAmount >= 0)
|
|
fScale = fAmount / 10.0f;
|
|
else
|
|
fScale = 4.0f;
|
|
if (flags & FT_NOMIPS)
|
|
nMips = 1;
|
|
else
|
|
{
|
|
// Compute log base 2 of width.
|
|
int bits = width;
|
|
int nlevelsw = 0;
|
|
for ( true; bits != 0; )
|
|
{
|
|
bits = bits >> 1;
|
|
nlevelsw++;
|
|
}
|
|
// Compute log base 2 of height.
|
|
bits = height;
|
|
int nlevelsh = 0;
|
|
for ( true; bits != 0; )
|
|
{
|
|
bits = bits >> 1;
|
|
nlevelsh++;
|
|
}
|
|
nMips = max(nlevelsw, nlevelsh);
|
|
}
|
|
int mx = width-1;
|
|
int my = height-1;
|
|
/*if (eTT == eTT_DSDTBump)
|
|
{
|
|
nMips = 1;
|
|
nSize = width*height*3;
|
|
dst = new byte[nSize];
|
|
BYTE* pDstT = (BYTE*)dst;
|
|
BYTE* pSrcB0 = (BYTE*)gray;
|
|
BYTE* pSrcB1 = ( pSrcB0 + width );
|
|
BYTE* pSrcB2 = ( pSrcB0 - width );
|
|
|
|
for(int y=0; y<height; y++ )
|
|
{
|
|
if( y == height-1 ) // Don't go past the last line
|
|
pSrcB1 = pSrcB0;
|
|
if( y == 0 ) // Don't go before first line
|
|
pSrcB2 = pSrcB0;
|
|
|
|
for( int x=0; x<width; x++ )
|
|
{
|
|
LONG v00 = *(pSrcB0+0); // Get the current pixel
|
|
LONG v01 = *(pSrcB0+1); // and the pixel to the right
|
|
LONG vM1 = *(pSrcB0-1); // and the pixel to the left
|
|
LONG v10 = *(pSrcB1+0); // and the pixel one line below.
|
|
LONG v1M = *(pSrcB2+0); // and the pixel one line above.
|
|
|
|
LONG iDu = (vM1-v01); // The delta-u bump value
|
|
LONG iDv = (v1M-v10); // The delta-v bump value
|
|
|
|
if( (v00 < vM1) && (v00 < v01) ) // If we are at valley
|
|
{
|
|
iDu = vM1-v00; // Choose greater of 1st order dif
|
|
if( iDu < v00-v01 )
|
|
iDu = v00-v01;
|
|
}
|
|
|
|
// The luminance bump value (land masses are less shiny)
|
|
WORD uL = ( v00>1 ) ? 63 : 127;
|
|
|
|
*pDstT++ = (BYTE)iDu;
|
|
*pDstT++ = (BYTE)iDv;
|
|
*pDstT++ = (BYTE)uL;
|
|
|
|
// Move one pixel to the left (src is 32-bpp)
|
|
pSrcB0+=1;
|
|
pSrcB1+=1;
|
|
pSrcB2+=1;
|
|
}
|
|
}
|
|
return dst;
|
|
}*/
|
|
|
|
TArray<Vec3d> *Normals = new TArray<Vec3d> [nMips];
|
|
TArray<byte> *Heights = new TArray<byte> [nMips];
|
|
nSize = width * height * 4;
|
|
if (!CRenderer::CV_r_texnormalmaptype)
|
|
{
|
|
Normals[0].Grow(width*height);
|
|
Heights[0].Grow(width*height);
|
|
Vec3d *vDst = &Normals[0][0];
|
|
byte *bDst = &Heights[0][0];
|
|
for(j=0; j<height; j++)
|
|
{
|
|
for(i=0; i<width; i++)
|
|
{
|
|
Vec3d vN;
|
|
vN.x = ((float)gray[j*width+((i-1)&mx)] - (float)gray[j*width+((i+1)&mx)]) / 255.0f;
|
|
vN.y = ((float)gray[((j-1)&my)*width+i] - (float)gray[((j+1)&my)*width+i]) / 255.0f;
|
|
if (bInv)
|
|
{
|
|
vN[0] = -vN[0];
|
|
vN[1] = -vN[1];
|
|
}
|
|
vN.x *= fScale;
|
|
vN.y *= fScale;
|
|
vN.z = 1.0f;
|
|
vN.NormalizeFast();
|
|
*vDst = vN;
|
|
vDst++;
|
|
*bDst = gray[j*width+i];
|
|
bDst++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Normals[0].Grow(width*height);
|
|
Vec3d *vDst = &Normals[0][0];
|
|
Heights[0].Grow(width*height);
|
|
byte *bDst = &Heights[0][0];
|
|
for(j=0; j<height; j++)
|
|
{
|
|
for(i=0; i<width; i++)
|
|
{
|
|
Vec3d vN1, vN2;
|
|
vN1.x = ((float)gray[j*width+i] - (float)gray[j*width+((i+1)&mx)]) / 255.0f;
|
|
vN1.y = ((float)gray[j*width+i] - (float)gray[((j+1)&my)*width+i]) / 255.0f;
|
|
if (bInv)
|
|
{
|
|
vN1.x = -vN1.x;
|
|
vN1.y = -vN1.y;
|
|
}
|
|
vN1.x *= fScale;
|
|
vN1.y *= fScale;
|
|
float f = vN1.y;
|
|
vN1.z = 1.0f;
|
|
vN1.NormalizeFast();
|
|
|
|
vN2.x = ((float)gray[((j+1)&my)*width+i] - (float)gray[((j+1)&my)*width+((i+1)&mx)]) / 255.0f;
|
|
vN2.y = f;
|
|
if (bInv)
|
|
{
|
|
vN2.x = -vN2.x;
|
|
vN2.y = -vN2.y;
|
|
}
|
|
vN2.x *= fScale;
|
|
vN2.z = 1.0f;
|
|
vN2.NormalizeFast();
|
|
|
|
*vDst = vN1 + vN2;
|
|
vDst->NormalizeFast();
|
|
vDst++;
|
|
|
|
*bDst = gray[j*width+i];
|
|
bDst++;
|
|
}
|
|
}
|
|
}
|
|
|
|
int reswp = width;
|
|
int reshp = height;
|
|
for (l=1; l<nMips; l++)
|
|
{
|
|
int resw = width >> l;
|
|
int resh = height >> l;
|
|
if (!resw)
|
|
resw = 1;
|
|
if (!resh)
|
|
resh = 1;
|
|
nSize += resw * resh * 4;
|
|
Normals[l].Grow(resw*resh);
|
|
Heights[l].Grow(resw*resh);
|
|
Vec3d *curr = &Normals[l][0];
|
|
Vec3d *prev = &Normals[l-1][0];
|
|
byte *bcurr = &Heights[l][0];
|
|
byte *bprev = &Heights[l-1][0];
|
|
int wmul = (reswp == 1) ? 1 : 2;
|
|
int hmul = (reshp == 1) ? 1 : 2;
|
|
for (j=0; j<resh; j++)
|
|
{
|
|
for (i=0; i<resw; i++)
|
|
{
|
|
Vec3d avg;
|
|
int bavg;
|
|
if (wmul == 1)
|
|
{
|
|
avg = prev[wmul*i+hmul*j*reswp] +
|
|
prev[wmul*i+(hmul*j+1)*reswp];
|
|
bavg = bprev[wmul*i+hmul*j*reswp] +
|
|
bprev[wmul*i+(hmul*j+1)*reswp];
|
|
bavg /= 2;
|
|
}
|
|
else
|
|
if (hmul == 1)
|
|
{
|
|
avg = prev[wmul*i+hmul*j*reswp] +
|
|
prev[wmul*i+1+hmul*j*reswp];
|
|
bavg = bprev[wmul*i+hmul*j*reswp] +
|
|
bprev[wmul*i+1+hmul*j*reswp];
|
|
bavg /= 2;
|
|
}
|
|
else
|
|
{
|
|
avg = prev[wmul*i+hmul*j*reswp] +
|
|
prev[wmul*i+1+hmul*j*reswp] +
|
|
prev[wmul*i+1+(hmul*j+1)*reswp] +
|
|
prev[wmul*i+(hmul*j+1)*reswp];
|
|
bavg = bprev[wmul*i+hmul*j*reswp] +
|
|
bprev[wmul*i+1+hmul*j*reswp] +
|
|
bprev[wmul*i+1+(hmul*j+1)*reswp] +
|
|
bprev[wmul*i+(hmul*j+1)*reswp];
|
|
bavg /= 4;
|
|
}
|
|
avg.NormalizeFast();
|
|
*curr = avg;
|
|
curr++;
|
|
*bcurr = bavg;
|
|
bcurr++;
|
|
}
|
|
}
|
|
reswp = resw;
|
|
reshp = resh;
|
|
}
|
|
dst = new byte[nSize];
|
|
int n = 0;
|
|
if (eTT == eTT_Bumpmap)
|
|
{
|
|
for (l=0; l<nMips; l++)
|
|
{
|
|
int resw = width >> l;
|
|
int resh = height >> l;
|
|
if (!resw)
|
|
resw = 1;
|
|
if (!resh)
|
|
resh = 1;
|
|
for (i=0; i<resw*resh; i++)
|
|
{
|
|
Vec3d vN = Normals[l][i];
|
|
byte bH = Heights[l][i];
|
|
dst[n*4+2] = (byte)(vN.x * 127.0f + 128.0f);
|
|
dst[n*4+1] = (byte)(vN.y * 127.0f + 128.0f);
|
|
dst[n*4+0] = (byte)(vN.z * 127.0f + 128.0f);
|
|
dst[n*4+3] = bH;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (eTT == eTT_DSDTBump)
|
|
{
|
|
for (l=0; l<nMips; l++)
|
|
{
|
|
int resw = width >> l;
|
|
int resh = height >> l;
|
|
if (!resw)
|
|
resw = 1;
|
|
if (!resh)
|
|
resh = 1;
|
|
for (i=0; i<resw*resh; i++)
|
|
{
|
|
Vec3d vN = Normals[l][i];
|
|
float f = vN.x * 127.5f;
|
|
dst[n*4+0] = (byte)(f);
|
|
f = vN.y * 127.5f;
|
|
dst[n*4+1] = (byte)(f);
|
|
dst[n*4+2] = (byte)(63.0f);
|
|
//dst[n*3+0] = byte( (vN.x>=0.0f) ? (vN.x* 127.5f) : (256.0f + vN.x * 127.5f));
|
|
//dst[n*3+1] = byte( (vN.y>=0.0f) ? (vN.y* 127.5f) : (256.0f + vN.y * 127.5f));
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] Heights;
|
|
delete [] Normals;
|
|
delete [] gray;
|
|
|
|
return dst;
|
|
}
|
|
|
|
void CTexMan::MergeNormalMaps(byte *src[2], CImageFile *im[2], int nMips[2])
|
|
{
|
|
int i, j, l, n;
|
|
|
|
int Width0 = im[0]->mfGet_width();
|
|
int Height0 = im[0]->mfGet_height();
|
|
int Width1 = im[1]->mfGet_width();
|
|
int Height1 = im[1]->mfGet_height();
|
|
n = 0;
|
|
int nIndexNM = (im[0]->mfGet_Flags() & FIM_NORMALMAP) ? 0 : 1;
|
|
|
|
if (Width0 == Width1 && Height0 == Height1)
|
|
{
|
|
for (l=0; l<nMips[0]; l++)
|
|
{
|
|
int wdt = Width0 >> l;
|
|
int hgt = Height0 >> l;
|
|
if (!wdt)
|
|
wdt = 1;
|
|
if (!hgt)
|
|
hgt = 1;
|
|
for (j=0; j<hgt; j++)
|
|
{
|
|
for (i=0; i<wdt; i++)
|
|
{
|
|
Vec3d vN[2];
|
|
vN[0].x = (src[0][n*4+2]/255.0f-0.5f)*2.0f;
|
|
vN[0].y = (src[0][n*4+1]/255.0f-0.5f)*2.0f;
|
|
vN[0].z = (src[0][n*4+0]/255.0f-0.5f)*2.0f;
|
|
|
|
vN[1].x = (src[1][n*4+2]/255.0f-0.5f)*2.0f;
|
|
vN[1].y = (src[1][n*4+1]/255.0f-0.5f)*2.0f;
|
|
vN[1].z = (src[1][n*4+0]/255.0f-0.5f)*2.0f;
|
|
|
|
float fLen = min(vN[nIndexNM].Length(), 1.0f);
|
|
|
|
vN[0].x /= vN[0].z;
|
|
vN[0].y /= vN[0].z;
|
|
vN[0].z = 1.0f;
|
|
|
|
vN[1].x /= vN[1].z;
|
|
vN[1].y /= vN[1].z;
|
|
vN[1].z = 1.0f;
|
|
|
|
vN[0] = vN[0] + vN[1];
|
|
vN[0].x *= 2.0f;
|
|
vN[0].y *= 2.0f;
|
|
vN[0].NormalizeFast();
|
|
vN[0] *= fLen;
|
|
vN[0].CheckMax(Vec3(-1,-1,-1));
|
|
vN[0].CheckMin(Vec3(1,1,1));
|
|
|
|
src[0][n*4+2] = (byte)(vN[0].x * 127.0f + 128.0f);
|
|
src[0][n*4+1] = (byte)(vN[0].y * 127.0f + 128.0f);
|
|
src[0][n*4+0] = (byte)(vN[0].z * 127.0f + 128.0f);
|
|
src[0][n*4+3] = (src[0][n*4+3] + src[1][n*4+3]) >> 1;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (l=0; l<nMips[0]; l++)
|
|
{
|
|
int wdt = Width0 >> l;
|
|
int hgt = Height0 >> l;
|
|
if (!wdt)
|
|
wdt = 1;
|
|
if (!hgt)
|
|
hgt = 1;
|
|
int l1 = min(l, nMips[1]-1);
|
|
int wdt1 = Width1 >> l1;
|
|
int hgt1 = Height1 >> l1;
|
|
if (!wdt1)
|
|
wdt1 = 1;
|
|
if (!hgt1)
|
|
hgt1 = 1;
|
|
int mwdt = wdt1-1;
|
|
int mhgt = hgt1-1;
|
|
for (j=0; j<hgt; j++)
|
|
{
|
|
for (i=0; i<wdt; i++)
|
|
{
|
|
Vec3d vN[2];
|
|
vN[0].x = (src[0][n*4+2]/255.0f-0.5f)*2.0f;
|
|
vN[0].y = (src[0][n*4+1]/255.0f-0.5f)*2.0f;
|
|
vN[0].z = (src[0][n*4+0]/255.0f-0.5f)*2.0f;
|
|
|
|
vN[1].x = (src[1][(i&mwdt)*4+(j&mhgt)*wdt1*4+2]/255.0f-0.5f)*2.0f;
|
|
vN[1].y = (src[1][(i&mwdt)*4+(j&mhgt)*wdt1*4+1]/255.0f-0.5f)*2.0f;
|
|
vN[1].z = (src[1][(i&mwdt)*4+(j&mhgt)*wdt1*4+0]/255.0f-0.5f)*2.0f;
|
|
|
|
float fLen = min(vN[nIndexNM].Length(), 1.0f);
|
|
|
|
vN[0].x /= vN[0].z;
|
|
vN[0].y /= vN[0].z;
|
|
vN[0].z = 1.0f;
|
|
|
|
vN[1].x /= vN[1].z;
|
|
vN[1].y /= vN[1].z;
|
|
vN[1].z = 1.0f;
|
|
|
|
vN[0] = vN[0] + vN[1];
|
|
vN[0].x *= 2.0f;
|
|
vN[0].y *= 2.0f;
|
|
vN[0].NormalizeFast();
|
|
vN[0] *= fLen;
|
|
vN[0].CheckMax(Vec3(-1,-1,-1));
|
|
vN[0].CheckMin(Vec3(1,1,1));
|
|
|
|
src[0][n*4+2] = (byte)(vN[0].x * 127.0f + 128.0f);
|
|
src[0][n*4+1] = (byte)(vN[0].y * 127.0f + 128.0f);
|
|
src[0][n*4+0] = (byte)(vN[0].z * 127.0f + 128.0f);
|
|
src[0][n*4+3] = (src[0][n*4+3] + src[1][(i&mwdt)*4+(j&mhgt)*wdt1*4+3]) >> 1;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTexMan::GenerateNormalMap(CImageFile** im, uint flags, uint flags2, byte eTT, float fAmount1, float fAmount2, STexPic *ti)
|
|
{
|
|
byte *dst[2];
|
|
int nMips[2];
|
|
int nSizeWithMips[2];
|
|
dst[0] = dst[1] = NULL;
|
|
nMips[0] = nMips[1] = 0;
|
|
nSizeWithMips[0] = nSizeWithMips[1] = 0;
|
|
|
|
if ((im[0]->mfGet_Flags() & FIM_NORMALMAP) || (im[0]->mfGet_Flags() & FIM_DSDT))
|
|
{
|
|
dst[0] = im[0]->mfGet_image();
|
|
nMips[0] = im[0]->mfGet_numMips();
|
|
nSizeWithMips[0] = im[0]->mfGet_ImageSize();
|
|
}
|
|
else
|
|
{
|
|
ETEX_Format eTF = sImageFormat2TexFormat(im[0]->m_eFormat);
|
|
dst[0] = GenerateNormalMap((byte *)im[0]->mfGet_image(), im[0]->mfGet_width(), im[0]->mfGet_height(), flags, flags2, eTT, fAmount1, ti, nMips[0], nSizeWithMips[0], eTF);
|
|
ti->m_ETF = eTF_8888;
|
|
}
|
|
|
|
if (im[1])
|
|
{
|
|
if ((im[1]->mfGet_Flags() & FIM_NORMALMAP) || (im[1]->mfGet_Flags() & FIM_DSDT))
|
|
{
|
|
dst[1] = im[1]->mfGet_image();
|
|
nMips[1] = im[1]->mfGet_numMips();
|
|
}
|
|
else
|
|
{
|
|
ETEX_Format eTF = sImageFormat2TexFormat(im[1]->m_eFormat);
|
|
dst[1] = GenerateNormalMap((byte *)im[1]->mfGet_image(), im[1]->mfGet_width(), im[1]->mfGet_height(), flags, flags2, eTT, fAmount2, ti, nMips[1], nSizeWithMips[1], eTF);
|
|
ti->m_ETF = eTF_8888;
|
|
}
|
|
}
|
|
if (flags & FT_NOMIPS)
|
|
nMips[0] = nMips[1] = 1;
|
|
|
|
if (dst[1])
|
|
{
|
|
MergeNormalMaps(dst, im, nMips);
|
|
if (!(im[1]->mfGet_Flags() & FIM_NORMALMAP) && !(im[1]->mfGet_Flags() & FIM_DSDT))
|
|
delete [] dst[1];
|
|
delete im[1];
|
|
im[1] = NULL;
|
|
if (nMips[0])
|
|
im[0]->mfSet_numMips(nMips[0]);
|
|
if (im[0]->m_pByteImage != dst[0])
|
|
{
|
|
SAFE_DELETE_ARRAY (im[0]->m_pByteImage);
|
|
im[0]->m_pByteImage = dst[0];
|
|
}
|
|
}
|
|
else
|
|
if ((im[0]->mfGet_Flags() & FIM_NORMALMAP) || (im[0]->mfGet_Flags() & FIM_DSDT))
|
|
return;
|
|
else
|
|
{
|
|
if (im[0]->m_pByteImage != dst[0])
|
|
{
|
|
SAFE_DELETE_ARRAY (im[0]->m_pByteImage);
|
|
im[0]->m_pByteImage = dst[0];
|
|
}
|
|
}
|
|
if (nMips[0])
|
|
im[0]->mfSet_numMips(nMips[0]);
|
|
if (eTT == eTT_Bumpmap)
|
|
{
|
|
im[0]->mfSet_Flags(FIM_NORMALMAP);
|
|
im[0]->m_eFormat = eIF_DDS_RGBA8;
|
|
}
|
|
else
|
|
if (eTT == eTT_DSDTBump)
|
|
{
|
|
im[0]->mfSet_Flags(FIM_DSDT);
|
|
im[0]->m_eFormat = eIF_DDS_DSDT;
|
|
}
|
|
im[0]->mfSet_ImageSize(nSizeWithMips[0]);
|
|
}
|
|
|
|
void CTexMan::BuildImageGamma(int wdt, int hgt, byte *dst, bool bHasAlpha)
|
|
{
|
|
}
|
|
|
|
void CTexMan::ImgResample(uint *uout, int ox, int oy, uint *uin, int ix, int iy)
|
|
{
|
|
int i, j;
|
|
uint *inrow;
|
|
uint ifrac, fracstep;
|
|
|
|
fracstep = ix*0x10000/ox;
|
|
if (!(ox & 3))
|
|
{
|
|
for (i=0 ; i<oy ; i++, uout += ox)
|
|
{
|
|
inrow = uin + ix*(i*iy/oy);
|
|
ifrac = fracstep >> 1;
|
|
for (j=0 ; j<ox ; j+=4)
|
|
{
|
|
uout[j] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
uout[j+1] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
uout[j+2] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
uout[j+3] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0 ; i<oy ; i++, uout += ox)
|
|
{
|
|
inrow = uin + ix*(i*iy/oy);
|
|
ifrac = fracstep >> 1;
|
|
for (j=0 ; j<ox ; j++)
|
|
{
|
|
uout[j] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTexMan::ImgResample8(byte *uout, int ox, int oy, byte *uin, int ix, int iy)
|
|
{
|
|
int i, j;
|
|
byte *inrow;
|
|
uint ifrac, fracstep;
|
|
|
|
fracstep = ix*0x10000/ox;
|
|
if (!(ox & 3))
|
|
{
|
|
for (i=0 ; i<oy ; i++, uout += ox)
|
|
{
|
|
inrow = uin + ix*(i*iy/oy);
|
|
ifrac = fracstep >> 1;
|
|
for (j=0 ; j<ox ; j+=4)
|
|
{
|
|
uout[j] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
uout[j+1] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
uout[j+2] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
uout[j+3] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0 ; i<oy ; i++, uout += ox)
|
|
{
|
|
inrow = uin + ix*(i*iy/oy);
|
|
ifrac = fracstep >> 1;
|
|
for (j=0 ; j<ox ; j++)
|
|
{
|
|
uout[j] = inrow[ifrac>>16];
|
|
ifrac += fracstep;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
byte *sData;
|
|
static TArray<byte> sAData;
|
|
void WriteDTXnFile (DWORD count, void *buffer, void * userData)
|
|
{
|
|
int n = sAData.Num();
|
|
sAData.Grow(count);
|
|
cryMemcpy(&sAData[n], buffer, count);
|
|
}
|
|
|
|
void ReadDTXnFile (DWORD count, void *buffer, void * userData)
|
|
{
|
|
cryMemcpy(buffer, sData, count);
|
|
sData += count;
|
|
}
|
|
|
|
#if !defined(_XBOX) && !defined(PS2) && !defined(LINUX)
|
|
#include <ddraw.h>
|
|
#else
|
|
|
|
#define DDSD_CAPS 0x00000001l // default
|
|
#define DDSD_PIXELFORMAT 0x00001000l
|
|
#define DDSD_WIDTH 0x00000004l
|
|
#define DDSD_HEIGHT 0x00000002l
|
|
#define DDSD_LINEARSIZE 0x00080000l
|
|
|
|
#define FOURCC_DXT1 (MAKEFOURCC('D','X','T','1'))
|
|
#define FOURCC_DXT2 (MAKEFOURCC('D','X','T','2'))
|
|
#define FOURCC_DXT3 (MAKEFOURCC('D','X','T','3'))
|
|
#define FOURCC_DXT4 (MAKEFOURCC('D','X','T','4'))
|
|
#define FOURCC_DXT5 (MAKEFOURCC('D','X','T','5'))
|
|
|
|
#endif
|
|
|
|
#include "Image/dds.h"
|
|
#include "dxtlib.h"
|
|
|
|
bool CTexMan::m_bRGBA = true;
|
|
|
|
byte *CTexMan::ImgConvertDXT_RGBA(byte *dst, STexPic *ti, int DXTSize)
|
|
{
|
|
int width;
|
|
int height;
|
|
int planes;
|
|
int lTotalWidth;
|
|
int rowBytes;
|
|
DDS_HEADER *ddsh;
|
|
|
|
byte *dd = new byte [DXTSize + sizeof(DDS_HEADER) + sizeof(DWORD)];
|
|
|
|
DWORD dwMagic = MAKEFOURCC('D','D','S',' ');
|
|
*(DWORD *)dd = dwMagic;
|
|
ddsh = (DDS_HEADER *)&dd[sizeof(DWORD)];
|
|
memset(ddsh, 0, sizeof(DDS_HEADER));
|
|
cryMemcpy(&dd[sizeof(DWORD)+sizeof(DDS_HEADER)], dst, DXTSize);
|
|
|
|
ddsh->dwSize = sizeof(DDS_HEADER);
|
|
ddsh->dwWidth = ti->m_Width;
|
|
ddsh->dwHeight = ti->m_Height;
|
|
ddsh->dwHeaderFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE;
|
|
int blockSize = (ti->m_Flags & FT_DXT1) ? 8 : 16;
|
|
ddsh->dwPitchOrLinearSize = ti->m_Width*ti->m_Height*4/blockSize;
|
|
if (ti->m_Flags & FT_DXT1)
|
|
ddsh->ddspf.dwFourCC = FOURCC_DXT1;
|
|
else
|
|
if (ti->m_Flags & FT_DXT3)
|
|
ddsh->ddspf.dwFourCC = FOURCC_DXT3;
|
|
else
|
|
if (ti->m_Flags & FT_DXT5)
|
|
ddsh->ddspf.dwFourCC = FOURCC_DXT5;
|
|
ddsh->ddspf.dwSize = sizeof(ddsh->ddspf);
|
|
ddsh->ddspf.dwFlags = DDS_FOURCC;
|
|
ddsh->dwSurfaceFlags = DDS_SURFACE_FLAGS_TEXTURE;
|
|
|
|
sData = dd;
|
|
|
|
#if defined(WIN64) || defined(LINUX)
|
|
// NOTE: AMD64 port: implement
|
|
dd = new byte [ti->m_Width*ti->m_Height*4];
|
|
#else
|
|
int src_format;
|
|
byte *_data = nvDXTdecompress(width, height, planes, lTotalWidth, rowBytes, src_format);
|
|
delete [] dd;
|
|
dd = new byte [ti->m_Width*ti->m_Height*4];
|
|
byte *dd1 = dd;
|
|
byte *data1 = _data;
|
|
|
|
if (planes == 3)
|
|
{
|
|
int n = width*height;
|
|
if (m_bRGBA)
|
|
for (int i=0; i<n; i++)
|
|
{
|
|
dd1[2] = data1[0];
|
|
dd1[1] = data1[1];
|
|
dd1[0] = data1[2];
|
|
dd1[3] = 255;
|
|
dd1 += 4;
|
|
data1 += 3;
|
|
}
|
|
else
|
|
for (int i=0; i<n; i++)
|
|
{
|
|
dd1[0] = data1[0];
|
|
dd1[1] = data1[1];
|
|
dd1[2] = data1[2];
|
|
dd1[3] = 255;
|
|
dd1 += 4;
|
|
data1 += 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int n = width*height;
|
|
if (m_bRGBA)
|
|
for (int i=0; i<n; i++)
|
|
{
|
|
dd1[2] = data1[0];
|
|
dd1[1] = data1[1];
|
|
dd1[0] = data1[2];
|
|
dd1[3] = data1[3];
|
|
dd1 += 4;
|
|
data1 += 4;
|
|
}
|
|
else
|
|
for (int i=0; i<n; i++)
|
|
{
|
|
dd1[0] = data1[0];
|
|
dd1[1] = data1[1];
|
|
dd1[2] = data1[2];
|
|
dd1[3] = data1[3];
|
|
dd1 += 4;
|
|
data1 += 4;
|
|
}
|
|
}
|
|
|
|
CRTDeleteArray(_data);
|
|
#endif
|
|
return dd;
|
|
}
|
|
|
|
byte *CTexMan::ImgConvertRGBA_DXT(byte *dst, STexPic *ti, int& DXTSize, int& nMips, int bits, bool bUseExistingMips)
|
|
{
|
|
#if !defined(PS2) && !defined(WIN64) && !defined(LINUX)
|
|
// NOTE: AMD64 port: implement
|
|
|
|
assert (bits == 24 || bits == 32);
|
|
|
|
CompressionOptions opt;
|
|
int i, j;
|
|
if (ti->m_Flags & FT_DXT1)
|
|
opt.TextureFormat = kDXT1;
|
|
else
|
|
if (ti->m_Flags & FT_DXT3)
|
|
opt.TextureFormat = kDXT3;
|
|
else
|
|
if (ti->m_Flags & FT_DXT5)
|
|
opt.TextureFormat = kDXT5;
|
|
|
|
int width = ti->m_Width;
|
|
int height = ti->m_Height;
|
|
byte *d;
|
|
|
|
if (bUseExistingMips)
|
|
{
|
|
int w = width;
|
|
int h = height;
|
|
TArray<byte> Data;
|
|
for (i=0; i<nMips; i++)
|
|
{
|
|
if (!w)
|
|
w = 1;
|
|
if (!h)
|
|
h = 1;
|
|
sAData.Free();
|
|
if (m_bRGBA)
|
|
{
|
|
if (bits == 24)
|
|
{
|
|
for (j=0; j<w*h; j++)
|
|
{
|
|
Exchange(dst[j*3+0], dst[j*3+2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j=0; j<w*h; j++)
|
|
{
|
|
Exchange(dst[j*4+0], dst[j*4+2]);
|
|
}
|
|
}
|
|
}
|
|
opt.MipMapType = dNoMipMaps;
|
|
nvDXTcompress(dst, w, h, w, &opt, bits/8, NULL);
|
|
if (bits == 24)
|
|
dst += w*h*3;
|
|
else
|
|
dst += w*h*4;
|
|
int Offs = sizeof(DWORD)+sizeof(DDS_HEADER);
|
|
int n = Data.Num();
|
|
int Size = sAData.Num()-Offs;
|
|
int blockSize = (ti->m_Flags & FT_DXT1) ? 8 : 16;
|
|
int mipsize = ((w+3)/4)*((h+3)/4)*blockSize;
|
|
assert(mipsize == Size);
|
|
Data.Grow(Size);
|
|
cryMemcpy(&Data[n], &sAData[Offs], Size);
|
|
w >>= 1;
|
|
h >>= 1;
|
|
}
|
|
d = new byte[Data.Num()];
|
|
cryMemcpy(d, &Data[0], Data.Num());
|
|
DXTSize = Data.Num();
|
|
}
|
|
else
|
|
{
|
|
sAData.Free();
|
|
opt.MipMapType = dGenerateMipMaps;
|
|
nvDXTcompress(dst, width, height, width, &opt, bits/8, NULL);
|
|
|
|
int Offs = sizeof(DWORD)+sizeof(DDS_HEADER);
|
|
d = new byte[sAData.Num()-Offs];
|
|
cryMemcpy(d, &sAData[Offs], sAData.Num()-Offs);
|
|
DXTSize = sAData.Num()-Offs;
|
|
sAData.Free();
|
|
}
|
|
|
|
if (!bUseExistingMips)
|
|
{
|
|
nMips = 0;
|
|
while (width || height)
|
|
{
|
|
if (!width)
|
|
width = 1;
|
|
if (!height)
|
|
height = 1;
|
|
nMips++;
|
|
|
|
width >>= 1;
|
|
height >>= 1;
|
|
}
|
|
}
|
|
|
|
return d;
|
|
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
void CTexMan::MipMap8Bit (STexPic *ti, byte *in, byte *out, int width, int height)
|
|
{
|
|
int i, j;
|
|
uint r,g,b;
|
|
byte *at1, *at2, *at3, *at4;
|
|
|
|
height >>= 1;
|
|
uint *tabsrc = ti->m_p8to24table;
|
|
byte *tabdst = ti->m_p15to8table;
|
|
for (i=0; i<height; i++,in+=width)
|
|
{
|
|
for (j=0; j<width; j+=2,out+=1,in+=2)
|
|
{
|
|
at1 = (byte *) (tabsrc + in[0]);
|
|
at2 = (byte *) (tabsrc + in[1]);
|
|
at3 = (byte *) (tabsrc + in[width+0]);
|
|
at4 = (byte *) (tabsrc + in[width+1]);
|
|
|
|
r = (at1[0]+at2[0]+at3[0]+at4[0]); r>>=5;
|
|
g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5;
|
|
b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5;
|
|
|
|
out[0] = tabdst[(r<<0) + (g<<5) + (b<<10)];
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTexMan::MipMap32Bit (STexPic *ti, byte *in, byte *out, int width, int height)
|
|
{
|
|
int i, j;
|
|
|
|
byte *src1 = in;
|
|
byte *dst1 = out;
|
|
int wd = width<<3;
|
|
for (i=0; i<height; i++,in+=width)
|
|
{
|
|
byte *src2 = src1;
|
|
for (j=0; j<width; j++)
|
|
{
|
|
dst1[0] = (src2[0]+src2[4]+src2[wd+0]+src2[wd+4])>>2;
|
|
dst1[1] = (src2[1]+src2[5]+src2[wd+1]+src2[wd+5])>>2;
|
|
dst1[2] = (src2[2]+src2[6]+src2[wd+2]+src2[wd+6])>>2;
|
|
dst1[3] = (src2[3]+src2[7]+src2[wd+3]+src2[wd+7])>>2;
|
|
dst1 += 4;
|
|
src2 += 8;
|
|
}
|
|
src1 += wd<<1;
|
|
}
|
|
}
|
|
|
|
void CTexMan::ClearAll(int nFlags)
|
|
{
|
|
if (nFlags == FRR_ALL)
|
|
Shutdown();
|
|
|
|
for (int i=0; i<m_Textures.Num(); i++)
|
|
{
|
|
STexPic *tp = m_Textures[i];
|
|
if (!tp)
|
|
continue;
|
|
|
|
if ((nFlags & FRR_RESTORE) && (tp->m_Flags & FT_NOREMOVE) && !(nFlags & FRR_SYSTEM))
|
|
continue;
|
|
|
|
if (!(tp->m_Flags & FT_NOREMOVE))
|
|
{
|
|
if (CRenderer::CV_r_printmemoryleaks)
|
|
iLog->Log("Warning: CTexMan::ClearAll: Texture %s (Id: %d) was not deleted (%d)", tp->m_SourceName.c_str(), i, tp->m_nRefCounter);
|
|
}
|
|
continue;
|
|
|
|
if (nFlags != FRR_ALL && (nFlags & FRR_REINITHW))
|
|
{
|
|
tp->ReleaseDriverTexture();
|
|
continue;
|
|
}
|
|
|
|
tp->Release(nFlags == FRR_ALL ? 2 : 1);
|
|
}
|
|
if (nFlags == FRR_ALL)
|
|
{
|
|
gRenDev->m_TexMan->m_Textures.Free();
|
|
gRenDev->m_TexMan->m_FreeSlots.Free();
|
|
}
|
|
}
|
|
|
|
#ifdef WIN64
|
|
#pragma warning( push ) //AMD Port
|
|
#pragma warning( disable : 4267 ) // conversion from 'size_t' to 'int', possible loss of data
|
|
#endif
|
|
|
|
void CTexMan::ReloadAll(int nFlags)
|
|
{
|
|
static char* cubefaces[5] = {"negx","posy","negy","posz","negz"};
|
|
|
|
int i, j;
|
|
|
|
for (i=0; i<m_Textures.Num(); i++)
|
|
{
|
|
STexPic *tp = m_Textures[i];
|
|
if (!tp || !tp->m_bBusy)
|
|
continue;
|
|
if (tp->m_LoadFrame == gRenDev->GetFrameID())
|
|
continue;
|
|
if (tp->m_Flags2 & FT2_RENDERTARGET)
|
|
continue;
|
|
tp->m_LoadFrame = gRenDev->GetFrameID();
|
|
if (tp->m_eTT == eTT_Cubemap)
|
|
{
|
|
char name[256];
|
|
StripExtension(*tp->m_SearchName, name);
|
|
int len = strlen(name);
|
|
if (len > 5)
|
|
{
|
|
for (j=0; j<5; j++)
|
|
{
|
|
if (!stricmp(&name[len-4], cubefaces[j]))
|
|
break;
|
|
}
|
|
if (j != 5)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (tp->m_pData32)
|
|
CreateTexture(*tp->m_SearchName, tp->m_Width, tp->m_Height, tp->m_Depth, tp->m_Flags, tp->m_Flags2, tp->m_pData32, tp->m_eTT, tp->m_fAmount1, tp->m_fAmount2, tp->m_DXTSize, tp, tp->m_Bind, tp->m_ETF);
|
|
else
|
|
gRenDev->EF_LoadTexture(*tp->m_SearchName, tp->m_Flags, tp->m_Flags2 | FT2_RELOAD, tp->m_eTT, tp->m_fAmount1, tp->m_fAmount2, tp->m_Id, tp->m_Bind);
|
|
}
|
|
}
|
|
|
|
#ifdef WIN64
|
|
#pragma warning( pop ) //AMD Port
|
|
#endif
|
|
|
|
bool CTexMan::ReloadFile(const char *fileName, int nFlags)
|
|
{
|
|
bool bRes = false;
|
|
int i;
|
|
char name[256];
|
|
strcpy(name, fileName);
|
|
strlwr(name);
|
|
ConvertDOSToUnixName(name, name);
|
|
|
|
for (i=0; i<m_Textures.Num(); i++)
|
|
{
|
|
STexPic *tp = m_Textures[i];
|
|
if (!tp || !tp->m_bBusy)
|
|
continue;
|
|
if (tp->m_LoadFrame == gRenDev->GetFrameID())
|
|
continue;
|
|
if (!strcmp(name, tp->m_SourceName.c_str()) && (tp->m_eTT != eTT_Cubemap || !tp->m_CubeSide))
|
|
{
|
|
tp->m_LoadFrame = gRenDev->GetFrameID();
|
|
iLog->Log("Reload texture '%s'", name);
|
|
gRenDev->EF_LoadTexture(*tp->m_SearchName, tp->m_Flags, tp->m_Flags2 | FT2_RELOAD, tp->m_eTT, tp->m_fAmount1, tp->m_fAmount2, tp->m_Id, tp->m_Bind);
|
|
bRes = true;
|
|
}
|
|
}
|
|
|
|
return bRes;
|
|
}
|
|
|
|
const char *STexPic::GetName()
|
|
{
|
|
return (m_Name.c_str());
|
|
}
|
|
|
|
int STexPic::GetWidth()
|
|
{
|
|
return m_Width;
|
|
}
|
|
int STexPic::GetHeight()
|
|
{
|
|
return m_Height;
|
|
}
|
|
int STexPic::GetTextureID()
|
|
{
|
|
return m_Bind;
|
|
}
|
|
bool STexPic::IsTextureLoaded()
|
|
{
|
|
assert(gRenDev);
|
|
return GetTextureID() != gRenDev->m_TexMan->m_Text_NoTexture->GetTextureID();
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
unsigned char id_length, colormap_type, image_type;
|
|
unsigned short colormap_index, colormap_length;
|
|
unsigned char colormap_size;
|
|
unsigned short x_origin, y_origin, width, height;
|
|
unsigned char pixel_size, attributes;
|
|
} TargaHeader_t;
|
|
|
|
void WriteTGA32(byte *dat, int wdt, int hgt, char *name);
|
|
|
|
|
|
///////////////////////////////////////////////////
|
|
bool STexPic::SaveTga(unsigned char *sourcedata,int sourceformat,int w,int h,const char *filename,bool flip)
|
|
{
|
|
/* if (flip)
|
|
{
|
|
int size=w*(sourceformat/8);
|
|
unsigned char *tempw=new unsigned char [size];
|
|
unsigned char *src1=sourcedata;
|
|
unsigned char *src2=sourcedata+(w*(sourceformat/8))*(h-1);
|
|
for (int k=0;k<h/2;k++)
|
|
{
|
|
memcpy(tempw,src1,size);
|
|
memcpy(src1,src2,size);
|
|
memcpy(src2,tempw,size);
|
|
src1+=size;
|
|
src2-=size;
|
|
}
|
|
delete [] tempw;
|
|
}*/
|
|
|
|
unsigned char * oldsourcedata = sourcedata;
|
|
|
|
if (sourceformat==FORMAT_8_BIT)
|
|
{
|
|
unsigned char *desttemp=new unsigned char [w*h*4];
|
|
memset(desttemp,0,w*h*3);
|
|
|
|
unsigned char *destptr=desttemp;
|
|
unsigned char *srcptr=sourcedata;
|
|
|
|
unsigned char col;
|
|
|
|
for (int k=0;k<w*h;k++)
|
|
{
|
|
col=*srcptr++;
|
|
*destptr++=col;
|
|
*destptr++=col;
|
|
*destptr++=col;
|
|
*destptr++=255;
|
|
}
|
|
|
|
sourcedata=desttemp;
|
|
|
|
sourceformat=FORMAT_32_BIT;
|
|
}
|
|
|
|
WriteTGA32(sourcedata, w, h, (char*)filename);
|
|
|
|
if (sourcedata!=oldsourcedata)
|
|
delete [] sourcedata;
|
|
|
|
return (true);
|
|
}
|
|
|
|
void CTexMan::LoadDefaultTextures()
|
|
{
|
|
char str[256];
|
|
|
|
m_Text_NoTexture = (STexPic*)gRenDev->EF_LoadTexture("Textures/red", FT_NOREMOVE | FT_NOSTREAM | FT_NORESIZE, 0, eTT_Base);
|
|
m_Text_White = (STexPic*)gRenDev->EF_LoadTexture("Textures/white", FT_NOREMOVE, FT2_NOANISO, eTT_Base);
|
|
m_Text_WhiteBump = (STexPic*)gRenDev->EF_LoadTexture("Textures/white_ddn", FT_NOREMOVE | FT_NORESIZE, FT2_NOANISO, eTT_Bumpmap);
|
|
m_Text_Atten2D = (STexPic*)gRenDev->EF_LoadTexture("Textures/Defaults/Pointlight2D", FT_NOREMOVE | FT_CLAMP | FT_NOSTREAM, FT2_NOANISO, eTT_Base);
|
|
m_Text_Edge = (STexPic*)gRenDev->EF_LoadTexture("Grad14", FT_NOREMOVE, FT2_NODXT, eTT_Base);
|
|
m_Text_Gray = (STexPic*)gRenDev->EF_LoadTexture("Grey", FT_NOREMOVE, FT2_NODXT, eTT_Base);
|
|
m_Text_FlashBangFlash = (STexPic*)gRenDev->EF_LoadTexture("Textures/flashbangflash", FT_NOREMOVE | FT_CLAMP| FT_NOSTREAM, 0, eTT_Base);
|
|
m_Text_ScreenNoise = (STexPic*)gRenDev->EF_LoadTexture("Textures/ScreenNoise", FT_NOREMOVE, 0, eTT_Base);
|
|
m_Text_HeatPalete= (STexPic*)gRenDev->EF_LoadTexture("Textures/Defaults/palletteHeat", FT_NORESIZE|FT_NOREMOVE | FT_CLAMP, FT2_NODXT, eTT_Base);
|
|
|
|
// Default Template textures
|
|
|
|
m_Text_ScreenMap = LoadTexture("$ScreenTexMap", FT_NOREMOVE | FT_NOSTREAM| FT_CLAMP, FT2_NODXT, eTT_Rectangle, -1.0f, -1.0f, TO_SCREENMAP);
|
|
m_Text_PrevScreenMap = LoadTexture("$PrevScreenTexMap", FT_NOREMOVE | FT_NOSTREAM| FT_CLAMP, FT2_NODXT, eTT_Rectangle, -1.0f, -1.0f, TO_PREVSCREENMAP);
|
|
m_Text_Glare = LoadTexture("$Glare", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_GLARE);
|
|
m_Text_ScreenLuminosityMap = LoadTexture("$ScreenLuminosityMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Rectangle, -1.0f, -1.0f, TO_SCREENLUMINOSITYMAP);
|
|
m_Text_ScreenCurrLuminosityMap = LoadTexture("$ScreenCurrLuminosityMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Rectangle, -1.0f, -1.0f, TO_SCREENCURRLUMINOSITYMAP);
|
|
m_Text_ScreenLowMap = LoadTexture("$ScreenLowMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Rectangle, -1.0f, -1.0f, TO_SCREENLOWMAP);
|
|
m_Text_ScreenAvg1x1 = LoadTexture("$ScreenAvg1x1", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Rectangle, -1.0f, -1.0f, TO_SCREENAVGMAP);
|
|
m_Text_FlashBangMap = LoadTexture("$FlashBangMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_FLASHBANGMAP);
|
|
m_Text_DofMap = LoadTexture("$DofTexMap", FT_NOREMOVE | FT_NOSTREAM| FT_CLAMP, FT2_NODXT, eTT_Rectangle, -1.0f, -1.0f, TO_DOFMAP);
|
|
|
|
m_Text_NormalizeCMap = LoadTexture("$NormalizeCMap", FT_NOREMOVE | FT_NOMIPS, FT2_NODXT, eTT_Cubemap, -1.0f, -1.0f, TO_NORMALIZE_CUBE_MAP);
|
|
m_Text_LightCMap = LoadTexture("$LightCMap", FT_NOREMOVE | FT_NOMIPS, FT2_NODXT, eTT_Cubemap, -1.0f, -1.0f, TO_LIGHT_CUBE_MAP);
|
|
|
|
m_Text_EnvLCMap = LoadTexture("$EnvironmentLightCMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Cubemap, -1.0f, -1.0f, TO_ENVIRONMENT_LIGHTCUBE_MAP);
|
|
m_Text_EnvCMap = LoadTexture("$EnvironmentCMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Cubemap, -1.0f, -1.0f, TO_ENVIRONMENT_CUBE_MAP);
|
|
m_Text_EnvTex = LoadTexture("$EnvironmentTex", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_ENVIRONMENT_TEX);
|
|
m_Text_EnvScr = LoadTexture("$EnvironmentScr", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Rectangle, -1.0f, -1.0f, TO_ENVIRONMENT_SCR);
|
|
m_Text_RefractMap = LoadTexture("$RefractMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_REFRACTMAP);
|
|
m_Text_RainMap = LoadTexture("$RainMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_RAINMAP);
|
|
m_Text_Ghost = LoadTexture("$Ghost", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_GHOST);
|
|
|
|
m_Text_WaterMap = LoadTexture("$WaterMap", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_TEXTURE_WATERMAP);
|
|
m_Text_WaterMap->m_Width = 512;
|
|
m_Text_WaterMap->m_Height = 512;
|
|
|
|
gRenDev->m_TexMan->m_Text_FromLight = LoadTexture("$FromLightCM", FT_NOREMOVE, FT2_NODXT, eTT_Cubemap, -1.0f, -1.0f, TO_FROMLIGHT);
|
|
|
|
int i;
|
|
|
|
for (i=0; i<8; i++)
|
|
{
|
|
sprintf(str, "$FromRE_%d", i);
|
|
m_Text_FromRE[i] = LoadTexture(str, FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_FROMRE0+i);
|
|
}
|
|
|
|
m_Text_FromObj = LoadTexture("$FromObj_0", FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_FROMOBJ);
|
|
|
|
for (i=0; i<MAX_ENVLIGHTCUBEMAPS; i++)
|
|
{
|
|
sprintf(str, "$EnvLCMap_%d", i);
|
|
m_EnvLCMaps[i].m_Id = i;
|
|
m_EnvLCMaps[i].m_Tex = LoadTexture(str, FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Cubemap, -1.0f, -1.0f, TO_ENVIRONMENT_LIGHTCUBE_MAP_REAL + i);
|
|
}
|
|
for (i=0; i<MAX_ENVCUBEMAPS; i++)
|
|
{
|
|
sprintf(str, "$EnvCMap_%d", i);
|
|
m_EnvCMaps[i].m_Id = i;
|
|
m_EnvCMaps[i].m_Tex = LoadTexture(str, FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Cubemap, -1.0f, -1.0f, TO_ENVIRONMENT_CUBE_MAP_REAL + i);
|
|
}
|
|
for (i=0; i<MAX_ENVTEXTURES; i++)
|
|
{
|
|
sprintf(str, "$EnvTex_%d", i);
|
|
m_EnvTexts[i].m_Id = i;
|
|
m_EnvTexts[i].m_Tex = LoadTexture(str, FT_PROJECTED | FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_ENVIRONMENT_TEX_MAP_REAL + i);
|
|
}
|
|
|
|
for (i=0; i<TO_CUSTOM_CUBE_MAP_LAST-TO_CUSTOM_CUBE_MAP_FIRST+1; i++)
|
|
{
|
|
sprintf(str, "$CustomCMap_%d", i);
|
|
m_CustomCMaps[i].m_Id = i;
|
|
m_CustomCMaps[i].m_Tex = LoadTexture(str, FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Cubemap, -1.0f, -1.0f, TO_CUSTOM_CUBE_MAP_FIRST + i);
|
|
}
|
|
for (i=0; i<TO_CUSTOM_TEXTURE_LAST-TO_CUSTOM_TEXTURE_FIRST+1; i++)
|
|
{
|
|
sprintf(str, "$CustomTexture_%d", i);
|
|
m_CustomTextures[i].m_Id = i;
|
|
m_CustomTextures[i].m_Tex = LoadTexture(str, FT_NOREMOVE | FT_NOSTREAM, FT2_NODXT, eTT_Base, -1.0f, -1.0f, TO_CUSTOM_TEXTURE_FIRST + i);
|
|
}
|
|
for (i=0; i<EFTT_MAX; i++)
|
|
{
|
|
m_Templates[i].m_Bind = EFTT_DIFFUSE + i;
|
|
m_Templates[i].m_Flags |= FT_NOREMOVE;
|
|
}
|
|
|
|
GenerateNMPalette();
|
|
GenerateFuncTextures();
|
|
}
|
|
|
|
void CTexMan::GenerateNMPalette()
|
|
{
|
|
int i, j, n;
|
|
int iInterpVal[16];
|
|
|
|
n = (int)((1.0f - cry_sqrtf(0.986159145832f)) * 127.0f + 0.5f);
|
|
iInterpVal[7] = 127 - n;
|
|
iInterpVal[8] = n + 128;
|
|
|
|
n = (int)((1.0f - cry_sqrtf(0.944636702538f)) * 127.0f + 0.5f);
|
|
iInterpVal[6] = 127 - n;
|
|
iInterpVal[9] = n + 128;
|
|
|
|
n = (int)((1.0f - cry_sqrtf(0.875432550907f)) * 127.0f + 0.5f);
|
|
iInterpVal[5] = 127 - n;
|
|
iInterpVal[10] = n + 128;
|
|
|
|
n = (int)((1.0f - cry_sqrtf(0.778546690941f)) * 127.0f + 0.5f);
|
|
iInterpVal[4] = 127 - n;
|
|
iInterpVal[11] = n + 128;
|
|
|
|
n = (int)((1.0f - cry_sqrtf(0.653979241848f)) * 127.0f + 0.5f);
|
|
iInterpVal[3] = 127 - n;
|
|
iInterpVal[12] = n + 128;
|
|
|
|
n = (int)((1.0f - cry_sqrtf(0.501730084419f)) * 127.0f + 0.5f);
|
|
iInterpVal[2] = 127 - n;
|
|
iInterpVal[13] = n + 128;
|
|
|
|
n = (int)((1.0f - cry_sqrtf(0.321799308062f)) * 127.0f + 0.5f);
|
|
iInterpVal[1] = 127 - n;
|
|
iInterpVal[14] = n + 128;
|
|
|
|
n = (int)((1.0f - cry_sqrtf(0.11418685317f)) * 127.0f + 0.5f);
|
|
iInterpVal[0] = 127 - n;
|
|
iInterpVal[15] = n + 128;
|
|
|
|
for (i=0; i<256; i++)
|
|
{
|
|
int n;
|
|
if (i <= iInterpVal[0])
|
|
{
|
|
m_NMPaletteLookup[i] = 0;
|
|
continue;
|
|
}
|
|
else
|
|
if (i >= iInterpVal[15])
|
|
{
|
|
m_NMPaletteLookup[i] = 0xf;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
n = 0;
|
|
while (true)
|
|
{
|
|
if (i <= iInterpVal[n+1])
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
if (i <= iInterpVal[n+2])
|
|
{
|
|
n++;
|
|
break;
|
|
}
|
|
else
|
|
if (i <= iInterpVal[n+3])
|
|
{
|
|
n += 2;
|
|
break;
|
|
}
|
|
else
|
|
if (i <= iInterpVal[n+4])
|
|
{
|
|
n += 3;
|
|
break;
|
|
}
|
|
else
|
|
if (i <= iInterpVal[n+5])
|
|
{
|
|
n += 4;
|
|
break;
|
|
}
|
|
else
|
|
if (i <= iInterpVal[n+6])
|
|
{
|
|
n += 5;
|
|
break;
|
|
}
|
|
else
|
|
if (i <= iInterpVal[n+7])
|
|
{
|
|
n += 6;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
n += 7;
|
|
if (n >= 14)
|
|
break;
|
|
}
|
|
}
|
|
if (i-iInterpVal[n] >= iInterpVal[n+1]-i)
|
|
n++;
|
|
m_NMPaletteLookup[i] = n;
|
|
}
|
|
}
|
|
|
|
Vec3d v;
|
|
for (i=0; i<16; i++)
|
|
{
|
|
v.x = (iInterpVal[i] - 127.5f) / 128.0f;
|
|
for (j=0; j<16; j++)
|
|
{
|
|
v.y = (iInterpVal[j] - 127.5f) / 128.0f;
|
|
float f = 1.0f - (v.x * v.x + v.y * v.y);
|
|
if (f < 0)
|
|
f = 0;
|
|
v.z = cry_sqrtf(f);
|
|
m_NMPalette[i][j].red = (byte)(cry_floorf(v.x * 127.0f + 0.5f) + 128.0f);
|
|
m_NMPalette[i][j].green = (byte)(cry_floorf(v.y * 127.0f + 0.5f) + 128.0f);
|
|
m_NMPalette[i][j].blue = (byte)(cry_floorf(v.z * 127.0f + 0.5f) + 128.0f);
|
|
m_NMPalette[i][j].alpha = 255;
|
|
}
|
|
}
|
|
m_NMPalette[15][15].red = 128;
|
|
m_NMPalette[15][15].green = 128;
|
|
m_NMPalette[15][15].blue = 128;
|
|
m_NMPalette[15][15].alpha = 255;
|
|
}
|
|
|
|
byte *CTexMan::ConvertNMToPalettedFormat(byte *src, STexPic *ti, ETEX_Format eTF)
|
|
{
|
|
int nMips = ti->m_nMips;
|
|
TArray<byte> IdxTex;
|
|
int w = ti->m_Width;
|
|
int h = ti->m_Height;
|
|
int l, i;
|
|
if (!nMips)
|
|
nMips = 1;
|
|
|
|
for (l=0; l<nMips; l++)
|
|
{
|
|
if (!w)
|
|
w = 1;
|
|
if (!h)
|
|
h = 1;
|
|
int nCur = IdxTex.Num();
|
|
int nSize = w*h;
|
|
IdxTex.Grow(nSize);
|
|
byte *dst = &IdxTex[nCur];
|
|
for (i=0; i<nSize; i++)
|
|
{
|
|
int j;
|
|
if (eTF == eTF_8888)
|
|
j = i*4;
|
|
else
|
|
j = i*3;
|
|
byte b;
|
|
if (src[j+0] == 0x80 && src[j+1] == 0x80 && src[j+2] == 0x80)
|
|
b = 255;
|
|
else
|
|
{
|
|
b = (m_NMPaletteLookup[src[j+0]]<<4) | m_NMPaletteLookup[src[j+1]];
|
|
if (b == 255)
|
|
b = 254;
|
|
}
|
|
dst[i] = b;
|
|
}
|
|
w >>= 1;
|
|
h >>= 1;
|
|
if (eTF == eTF_8888)
|
|
src += nSize * 4;
|
|
else
|
|
src += nSize * 3;
|
|
}
|
|
byte *dst = new byte [IdxTex.Num()];
|
|
cryMemcpy(dst, &IdxTex[0], IdxTex.Num());
|
|
|
|
return dst;
|
|
}
|
|
|
|
void CTexMan::SetGridTexture(STexPic *tp)
|
|
{
|
|
int i, j;
|
|
if (tp->m_Width < 8 && tp->m_Height < 8)
|
|
{
|
|
gRenDev->SetTexture(TX_FIRSTBIND, eTT_Base);
|
|
return;
|
|
}
|
|
for (i=0; i<m_TGrids.Num(); i++)
|
|
{
|
|
STexGrid *tg = &m_TGrids[i];
|
|
if (tg->m_Width == tp->m_Width && tg->m_Height == tp->m_Height && tg->m_TP->m_eTT == tp->m_eTT)
|
|
break;
|
|
}
|
|
if (i != m_TGrids.Num())
|
|
{
|
|
m_TGrids[i].m_TP->Set();
|
|
return;
|
|
}
|
|
STexGrid tg;
|
|
tg.m_Width = tp->m_Width;
|
|
tg.m_Height = tp->m_Height;
|
|
STexPic *tpg = LoadTexture("Textures/world_texel", 0, 0, tp->m_eTT);
|
|
if (tpg->m_Width == tp->m_Height && tpg->m_Height == tp->m_Height && tpg->m_eTT == tp->m_eTT)
|
|
{
|
|
tg.m_TP = tpg;
|
|
tg.m_TP->Set();
|
|
m_TGrids.AddElem(tg);
|
|
return;
|
|
}
|
|
byte *src = tpg->GetData32();
|
|
if (!src)
|
|
{
|
|
gRenDev->SetTexture(TX_FIRSTBIND, eTT_Base);
|
|
return;
|
|
}
|
|
byte *dst = new byte[tg.m_Width*tg.m_Height*4];
|
|
|
|
int wdt = tg.m_Width;
|
|
int hgt = tg.m_Height;
|
|
int wdt1 = tpg->m_Width;
|
|
int hgt1 = tpg->m_Height;
|
|
int mwdt1 = wdt1-1;
|
|
int mhgt1 = hgt1-1;
|
|
for (j=0; j<hgt; j++)
|
|
{
|
|
for (i=0; i<wdt; i++)
|
|
{
|
|
dst[j*wdt*4+i*4+0] = src[(i&mwdt1)*4+(j&mhgt1)*wdt1*4+0];
|
|
dst[j*wdt*4+i*4+1] = src[(i&mwdt1)*4+(j&mhgt1)*wdt1*4+1];
|
|
dst[j*wdt*4+i*4+2] = src[(i&mwdt1)*4+(j&mhgt1)*wdt1*4+2];
|
|
dst[j*wdt*4+i*4+3] = src[(i&mwdt1)*4+(j&mhgt1)*wdt1*4+3];
|
|
}
|
|
}
|
|
char name[128];
|
|
sprintf(name, "TexGrid_%d_%d", wdt, hgt);
|
|
tg.m_TP = CreateTexture(name, wdt, hgt, 1, 0, 0, dst, tp->m_eTT);
|
|
delete [] dst;
|
|
delete [] src;
|
|
tg.m_TP->Set();
|
|
m_TGrids.AddElem(tg);
|
|
}
|
|
|
|
void CTexMan::PreloadTextures (int Flags)
|
|
{
|
|
int i;
|
|
for (i=0; i<gRenDev->m_TexMan->m_Textures.Num(); i++)
|
|
{
|
|
STexPic *tp = gRenDev->m_TexMan->m_Textures[i];
|
|
if (!tp || tp->m_Bind == TX_FIRSTBIND)
|
|
continue;
|
|
if (tp->m_Flags2 & FT2_RENDERTARGET)
|
|
continue;
|
|
tp->Preload(Flags);
|
|
}
|
|
}
|
|
|
|
bool CTexMan::GetCubeColor(Vec3 *Pos, CFColor *cols)
|
|
{
|
|
CRendElement tre;
|
|
CRendElement *re = gRenDev->m_RP.m_pRE;
|
|
CCObject *obj = gRenDev->m_RP.m_pCurObject;
|
|
gRenDev->m_RP.m_pRE = &tre;
|
|
gRenDev->m_RP.m_pCurObject = gRenDev->m_RP.m_TempObjects[0];
|
|
SEnvTexture *cm = &gRenDev->m_TexMan->m_EnvLCMaps[0];
|
|
cm->m_ObjPos = Vec3(0,0,0);
|
|
cm->m_CamPos = *Pos;
|
|
cm->m_bReady = false;
|
|
ScanEnvironmentCube(cm, -1, 16, true);
|
|
for (int n=0; n<6; n++)
|
|
{
|
|
GetAverageColor(cm, n);
|
|
cols[n].r = cm->m_EnvColors[n].bcolor[0] / 255.0f;
|
|
cols[n].g = cm->m_EnvColors[n].bcolor[1] / 255.0f;
|
|
cols[n].b = cm->m_EnvColors[n].bcolor[2] / 255.0f;
|
|
cols[n].a = cm->m_EnvColors[n].bcolor[3] / 255.0f;
|
|
}
|
|
return true;
|
|
}
|