Material texture animation support

Use by having texmap entries of the form "Mat-Tex1-Tex2-...". Right
now the speed is hard-coded to one phase per second.

The general idea is that the 3D texture contains all texture
transitions somewhere in the form of two textures with neighbouring
3D coordinates. There's some room for optimization here, of which
the code exploits some. Being smart can be arbitrarily hard actually.
Peter Wortmann 2011-08-20 01:25:08 +01:00
parent 6c156a22a7
commit c0df3afedc
4 changed files with 194 additions and 67 deletions

View File

@ -37,7 +37,7 @@ float queryMatMap(int pix)
{
#ifdef BROKEN_ARRAYS_WORKAROUND
int idx = f2i(texture1D(matMapTex, float(pix) / 256.0 + 0.5 / 256.0).r);
return (float(idx) + 0.5) / float(materialDepth);
return float(idx) / 256.0 + 0.5 / float(materialDepth);
#else
return matMap[pix];
#endif

View File

@ -16,12 +16,12 @@
// Automatically reload shaders when changed at runtime?
#define AUTO_RELOAD_SHADERS
#ifdef _DEBUG
// Generate seperator textures into 3D texture so we can make sure that
// we are addressing textures using the right coordinates
#define DEBUG_SEPERATOR_TEXTURES
#ifdef _DEBUG
// Replace all textures by solid colors
//#define DEBUG_SOLID_COLOR_TEXTURES
@ -40,6 +40,9 @@ const char *C4LR_ShaderWorkarounds[] = {
};
const int C4LR_ShaderWorkaroundCount = sizeof(C4LR_ShaderWorkarounds) / sizeof(*C4LR_ShaderWorkarounds);
// Name used for the seperator texture
const char *const SEPERATOR_TEXTURE = "--SEP--";
// Map of uniforms to names in shader
static const char *GetUniformName(int iUniform)
{
@ -59,8 +62,7 @@ static const char *GetUniformName(int iUniform)
C4LandscapeRenderGL::C4LandscapeRenderGL()
: iLandscapeShaderTime(0),
hVert(0), hFrag(0), hProg(0),
iTexCount(0)
hVert(0), hFrag(0), hProg(0)
{
ZeroMem(Surfaces, sizeof(Surfaces));
ZeroMem(hMaterialTexture, sizeof(hMaterialTexture));
@ -92,11 +94,6 @@ bool C4LandscapeRenderGL::Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pT
this->iHeight = iHeight;
this->pTexs = pTexs;
// Count the textures
iTexCount = 0;
while(pTexs->GetTexture(iTexCount))
iTexCount++;
// Build texture, er, texture
if (!InitMaterialTexture(pTexs))
{
@ -145,16 +142,14 @@ void C4LandscapeRenderGL::Clear()
bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
{
#ifdef DEBUG_SEPERATOR_TEXTURES
int iTexCountP = 2 * iTexCount;
#else
int iTexCountP = iTexCount;
#endif
// Determine depth to use. Might have more complicated
// mappings in future.
// Populate our map with all needed textures
MaterialTextureMap.push_back(StdCopyStrBuf(""));
AddTexturesFromMap(pTexs);
// Determine depth to use
iMaterialTextureDepth = 1;
while(iMaterialTextureDepth < iTexCountP + 1)
while(iMaterialTextureDepth < int32_t(MaterialTextureMap.size()))
iMaterialTextureDepth <<= 1;
// Find first (actual) texture
@ -189,48 +184,53 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
for(int i = 0; i < iMaterialTextureDepth; i++)
{
BYTE *p = pData + i * iTexSize;
#ifdef DEBUG_SEPERATOR_TEXTURES
if (i % 2)
// Get texture at position
const char *szTexture;
if(i < int32_t(MaterialTextureMap.size()))
szTexture = MaterialTextureMap[i].getData();
else
szTexture = "";
// Try to find the texture
C4Texture *pTex; CSurface *pSurface;
if((pTex = pTexs->GetTexture(szTexture)) && (pSurface = pTex->Surface32))
{
// Make every second texture ugly stripes
#ifdef DEBUG_SOLID_COLOR_TEXTURES
// Just write a solid color that depends on the texture index
DWORD *texdata = reinterpret_cast<DWORD *>(p);
for (int y = 0; y < iTexHgt; ++y)
for (int x = 0; x < iTexWdt; ++x)
*texdata++ = RGBA((iTex & 48), (iTex & 3) * 16, (i & 12) * 4, 255);
continue;
#else
if(pSurface->iTexX != 1 || pSurface->iTexY != 1)
Log(" gl: Halp! Material texture is fragmented!");
else
{
// Size recheck
if(pSurface->Wdt != iTexWdt || pSurface->Hgt != iTexHgt)
LogF(" gl: texture %s size mismatch (%dx%d vs %dx%d)!", szTexture, pSurface->Wdt, pSurface->Hgt, iTexWdt, iTexHgt);
// Copy bytes
DWORD *texdata = reinterpret_cast<DWORD *>(p);
pSurface->Lock();
for (int y = 0; y < iTexHgt; ++y)
for (int x = 0; x < iTexWdt; ++x)
*texdata++ = pSurface->GetPixDw(x % pSurface->Wdt, y % pSurface->Hgt, false);
pSurface->Unlock();
continue;
}
#endif
}
// Seperator texture?
if(SEqual(szTexture, SEPERATOR_TEXTURE))
{
// Make some ugly stripes
DWORD *texdata = reinterpret_cast<DWORD *>(p);
for (int y = 0; y < iTexHgt; ++y)
for (int x = 0; x < iTexWdt; ++x)
*texdata++ = ((x + y) % 32 < 16 ? RGBA(255, 0, 0, 255) : RGBA(0, 255, 255, 255));
continue;
}
int iTex = i / 2;
#else
int iTex = i;
#endif
#ifdef DEBUG_SOLID_COLOR_TEXTURES
DWORD *texdata = reinterpret_cast<DWORD *>(p);
for (int y = 0; y < iTexHgt; ++y)
for (int x = 0; x < iTexWdt; ++x)
*texdata++ = RGBA((iTex & 48), (iTex & 3) * 16, (i & 12) * 4, 255);
continue;
#endif
C4Texture *pTex; CSurface *pSurface;
if(!(pTex = pTexs->GetTexture(pTexs->GetTexture(iTex))))
{}
else if(!(pSurface = pTex->Surface32))
{}
else if(pSurface->iTexX != 1 || pSurface->iTexY != 1)
Log(" gl: Halp! Material texture is fragmented!");
else
{
// Size recheck
if(pSurface->Wdt != iTexWdt || pSurface->Hgt != iTexHgt)
LogF(" gl: texture %s size mismatch (%dx%d vs %dx%d)!", pTexs->GetTexture(iTex), pSurface->Wdt, pSurface->Hgt, iTexWdt, iTexHgt);
// Copy bytes
DWORD *texdata = reinterpret_cast<DWORD *>(p);
pSurface->Lock();
for (int y = 0; y < iTexHgt; ++y)
for (int x = 0; x < iTexWdt; ++x)
*texdata++ = pSurface->GetPixDw(x % pSurface->Wdt, y % pSurface->Hgt, false);
pSurface->Unlock();
continue;
}
// If we didn't "continue" yet, we haven't written the texture yet. Make it transparent.
memset(p, 0, iTexSize);
}
@ -731,6 +731,97 @@ void C4LandscapeRenderGL::RefreshShaders()
}
}
int32_t C4LandscapeRenderGL::LookupTextureTransition(const char *szFrom, const char *szTo)
{
// Is this actually a transition? Otherwise we're looking for a single texture
bool fTransit = !SEqual(szFrom, szTo);
// Look for a position in the map where the textures appear in sequence
uint32_t i;
for(i = 1; i < MaterialTextureMap.size(); i++)
{
if(SEqual(szFrom, MaterialTextureMap[i].getData()))
{
// Single texture: We're done
if(!fTransit) return i;
// Check next texture as well
if(i + 1 >= MaterialTextureMap.size())
return -1;
if(SEqual(szTo, MaterialTextureMap[i+1].getData()))
return i;
}
}
return -1;
}
void C4LandscapeRenderGL::AddTextureTransition(const char *szFrom, const char *szTo)
{
// Empty?
if (!szFrom || !szTo) return;
// First try the lookup (both directions)
if (LookupTextureTransition(szFrom, szTo) >= 0) return;
if (LookupTextureTransition(szTo, szFrom) >= 0) return;
// Single texture? Add it as single
if (SEqual(szTo, szFrom))
MaterialTextureMap.push_back(StdCopyStrBuf(szFrom));
// Have one of the textures at the end of the list?
else if(SEqual(MaterialTextureMap.back().getData(), szFrom))
MaterialTextureMap.push_back(StdCopyStrBuf(szTo));
else if(SEqual(MaterialTextureMap.back().getData(), szTo))
MaterialTextureMap.push_back(StdCopyStrBuf(szFrom));
else
{
// Otherwise add both
MaterialTextureMap.push_back(StdCopyStrBuf(szFrom));
MaterialTextureMap.push_back(StdCopyStrBuf(szTo));
}
}
void C4LandscapeRenderGL::AddTextureAnim(const char *szTextureAnim)
{
if(!szTextureAnim) return;
#ifdef DEBUG_SEPERATOR_TEXTURES
// Save back count of textures at start
uint32_t iStartTexCount = MaterialTextureMap.size();
#endif
// Add all individual transitions
const char *pFrom = szTextureAnim;
for(;;)
{
// Get next phase
const char *pTo = strchr(pFrom, '-');
if(!pTo) pTo = szTextureAnim; else pTo++;
// Add transition
StdStrBuf From, To;
From.CopyUntil(pFrom, '-');
To.CopyUntil(pTo, '-');
AddTextureTransition(From.getData(), To.getData());
// Advance
if(pTo == szTextureAnim) break;
pFrom = pTo;
}
#ifdef DEBUG_SEPERATOR_TEXTURES
// Add a seperator texture, if we added any new ones
if(MaterialTextureMap.size() > iStartTexCount)
MaterialTextureMap.push_back(StdCopyStrBuf(SEPERATOR_TEXTURE));
#endif
}
void C4LandscapeRenderGL::AddTexturesFromMap(C4TextureMap *pMap)
{
// Go through used texture (animations) and add all phases to our map
// Note: We can be smarter here, for example add longer animations
// first in order to make better reuse of 3D texture slots.
// We could even make a full-blown optimization problem out of it.
// Future work...
const C4TexMapEntry *pEntry;
for(int32_t i = 0; pEntry = pMap->GetEntry(i); i++)
// ToDo: Properly handle jumping back
AddTextureAnim(pEntry->GetTextureName());
}
void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap)
{
// TODO: Still merely an inefficient placeholder for things to come...
@ -742,19 +833,48 @@ void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap)
const C4TexMapEntry *pEntry = pTexs->GetEntry(PixCol2Tex(BYTE(pix)));
if(!pEntry->GetTextureName())
{
// Textures over iTexCount are transparent
if(pFMap) pFMap[pix] = (float(iMaterialTextureDepth) - 1.5) / iMaterialTextureDepth;
if(pIMap) pIMap[pix] = iMaterialTextureDepth - 2;
// Undefined textures transparent
if(pFMap) pFMap[pix] = 0.5 / iMaterialTextureDepth;
if(pIMap) pIMap[pix] = 0;
continue;
}
// Got animation?
int iPhases = 1; const char *p = pEntry->GetTextureName();
if(p = strchr(p, '-')) { p++; iPhases++; }
// Hard-coded hack. Fix me!
const int iPhaseLength = 1000;
float phase = (iPhases == 1 ? 0 : float(GetTime() % (iPhases * iPhaseLength)) / iPhaseLength);
// Find our transition
const char *pFrom = pEntry->GetTextureName();
float gTexCoo = 0;
for(int iP = 0;; iP++)
{
// Get next phase
const char *pTo = strchr(pFrom, '-');
if(!pTo) pTo = pEntry->GetTextureName(); else pTo++;
// Add transition
if(iP == int(phase))
{
StdStrBuf From, To;
From.CopyUntil(pFrom, '-');
To.CopyUntil(pTo, '-');
// Find transition
int iTrans;
if ((iTrans = LookupTextureTransition(From.getData(), To.getData())) >= 0)
gTexCoo = float(iTrans) + fmod(phase, 1.0f);
else if ((iTrans = LookupTextureTransition(To.getData(), From.getData())) >= 0)
gTexCoo = float(iTrans) + 1.0 - fmod(phase, 1.0f);
break;
}
// Advance
pFrom = pTo;
}
// Assign texture
int32_t iTexIndex = pTexs->GetTextureIndex(pEntry->GetTextureName());
if(iTexIndex < 0) iTexIndex = 0;
#ifdef DEBUG_SEPERATOR_TEXTURES
iTexIndex *= 2;
#endif
if(pFMap) pFMap[pix] = (float(iTexIndex) + 0.5) / iMaterialTextureDepth;
if(pIMap) pIMap[pix] = iTexIndex;
if(pFMap) pFMap[pix] = (gTexCoo + 0.5) / iMaterialTextureDepth;
if(pIMap) pIMap[pix] = int((gTexCoo * 256.0 / iMaterialTextureDepth) + 0.5);
}
}

View File

@ -90,16 +90,17 @@ private:
// shader variables
GLhandleARB hUniforms[C4LRU_Count];
// Texture count
int32_t iTexCount;
// 3D texture of material textures
GLuint hMaterialTexture[C4LR_MipMapCount];
// material texture positions in 3D texture
std::vector<StdCopyStrBuf> MaterialTextureMap;
// depth of material texture in layers
int32_t iMaterialTextureDepth;
// scaler image
C4FacetSurface fctScaler;
public:
virtual bool Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pMap, C4GroupSet *pGraphics);
virtual void Clear();
@ -123,6 +124,10 @@ private:
bool InitShaders();
void ClearShaders();
int32_t LookupTextureTransition(const char *szFrom, const char *szTo);
void AddTextureTransition(const char *szFrom, const char *szTo);
void AddTextureAnim(const char *szTextureAnim);
void AddTexturesFromMap(C4TextureMap *pMap);
void BuildMatMap(GLfloat *pFMap, GLubyte *pIMap);
};
#endif

View File

@ -80,10 +80,12 @@ bool C4TexMapEntry::Init()
}
pMaterial = &::MaterialMap.Map[iMaterialIndex];
// Find texture
C4Texture * sfcTexture = ::TextureMap.GetTexture(Texture.getData());
StdStrBuf FirstTexture;
FirstTexture.CopyUntil(Texture.getData(), '-');
C4Texture * sfcTexture = ::TextureMap.GetTexture(FirstTexture.getData());
if (!sfcTexture)
{
DebugLogF("Error initializing material %s-%s: Invalid texture!", Material.getData(), Texture.getData());
DebugLogF("Error initializing material %s-%s: Invalid texture!", Material.getData(), FirstTexture.getData());
Clear();
return false;
}