Skip to content

Commit

Permalink
Replace 3D texture in landscape shader by a 2D texture array
Browse files Browse the repository at this point in the history
A texture array is conceptionally what should be used in this case. One
advantage of this is that we don't have to generate mipmaps ourselves but can
let the graphics driver take care of it. Same for selection of the mipmap
level. This would even allow to choose different mipmap levels for different
textures.

This is a somewhat experimental change since it makes OpenGL 3.0 a hard
requirement for OpenClonk. I expect that this is fine, but if this causes
failures during landscape creation on common hardware/drivers we should
revisit.
  • Loading branch information
aburgm committed Sep 17, 2015
1 parent 061305c commit 790219a
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 90 deletions.
17 changes: 9 additions & 8 deletions planet/Graphics.ocg/LandscapeShader.glsl
Expand Up @@ -3,7 +3,7 @@
// Input textures // Input textures
uniform sampler2D landscapeTex[2]; uniform sampler2D landscapeTex[2];
uniform sampler2D scalerTex; uniform sampler2D scalerTex;
uniform sampler3D materialTex; uniform sampler2DArray materialTex;


// Resolution of the landscape texture // Resolution of the landscape texture
uniform vec2 resolution; uniform vec2 resolution;
Expand All @@ -16,7 +16,7 @@ uniform sampler1D matMapTex;
#else #else
uniform float matMap[256]; uniform float matMap[256];
#endif #endif
uniform int materialDepth; uniform float materialDepth;
uniform vec2 materialSize; uniform vec2 materialSize;


// Expected parameters for the scaler // Expected parameters for the scaler
Expand All @@ -35,9 +35,10 @@ float queryMatMap(int pix)
{ {
#ifndef NO_BROKEN_ARRAYS_WORKAROUND #ifndef NO_BROKEN_ARRAYS_WORKAROUND
int idx = f2i(texture1D(matMapTex, float(pix) / 256.0 + 0.5 / 256.0).r); int idx = f2i(texture1D(matMapTex, float(pix) / 256.0 + 0.5 / 256.0).r);
return float(idx) / 256.0 + 0.5 / float(materialDepth); return (float(idx) / 256.0 + 0.0 / materialDepth) * materialDepth;
//return texture1D(matMapTex, float(pix) / 256.0 + 0.5 / 256.0).r;
#else #else
return matMap[pix]; return matMap[pix] * materialDepth;
#endif #endif
} }


Expand Down Expand Up @@ -70,14 +71,14 @@ slice(material)


// Get material pixels // Get material pixels
float materialIx = queryMatMap(f2i(landscapePx.r)); float materialIx = queryMatMap(f2i(landscapePx.r));
vec4 materialPx = texture3D(materialTex, vec3(materialCoo, materialIx)); vec4 materialPx = texture(materialTex, vec3(materialCoo, materialIx));
vec4 normalPx = texture3D(materialTex, vec3(materialCoo, materialIx+0.5)); vec4 normalPx = texture(materialTex, vec3(materialCoo, materialIx+0.5*materialDepth));


// Same for second pixel, but we'll simply use the first normal // Same for second pixel, but we'll simply use the first normal
#ifdef OC_HAVE_2PX #ifdef OC_HAVE_2PX
float materialIx2 = queryMatMap(f2i(landscapePx2.r)); float materialIx2 = queryMatMap(f2i(landscapePx2.r));
vec4 materialPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2)); vec4 materialPx2 = texture(materialTex, vec3(materialCoo, materialIx2));
vec4 normalPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2+0.5)); vec4 normalPx2 = texture(materialTex, vec3(materialCoo, materialIx2+0.5*materialDepth));
#endif #endif
} }


Expand Down
2 changes: 1 addition & 1 deletion src/graphics/C4Shader.h
Expand Up @@ -22,7 +22,7 @@
#include "C4Surface.h" #include "C4Surface.h"


// Shader version // Shader version
const int C4Shader_Version = 120; // GLSL 1.20 / OpenGL 2.1 const int C4Shader_Version = 130; // GLSL 1.30 / OpenGL 3.0


// Maximum number of texture coordinates // Maximum number of texture coordinates
const int C4Shader_MaxTexCoords = 8; const int C4Shader_MaxTexCoords = 8;
Expand Down
116 changes: 41 additions & 75 deletions src/landscape/C4LandscapeRender.cpp
Expand Up @@ -54,7 +54,7 @@ const char *const SEPERATOR_TEXTURE = "--SEP--";
C4LandscapeRenderGL::C4LandscapeRenderGL() C4LandscapeRenderGL::C4LandscapeRenderGL()
{ {
ZeroMem(Surfaces, sizeof(Surfaces)); ZeroMem(Surfaces, sizeof(Surfaces));
ZeroMem(hMaterialTexture, sizeof(hMaterialTexture)); hMaterialTexture = 0;
} }


C4LandscapeRenderGL::~C4LandscapeRenderGL() C4LandscapeRenderGL::~C4LandscapeRenderGL()
Expand Down Expand Up @@ -135,8 +135,8 @@ void C4LandscapeRenderGL::Clear()
delete Surfaces[i]; delete Surfaces[i];
Surfaces[i] = NULL; Surfaces[i] = NULL;
} }
glDeleteTextures(C4LR_MipMapCount, hMaterialTexture); if (hMaterialTexture) glDeleteTextures(1, &hMaterialTexture);
std::fill_n(hMaterialTexture, C4LR_MipMapCount, 0); hMaterialTexture = 0;
} }


bool C4LandscapeRenderGL::InitLandscapeTexture() bool C4LandscapeRenderGL::InitLandscapeTexture()
Expand Down Expand Up @@ -170,9 +170,7 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
AddTexturesFromMap(pTexs); AddTexturesFromMap(pTexs);


// Determine depth to use // Determine depth to use
iMaterialTextureDepth = 1; iMaterialTextureDepth = 2*MaterialTextureMap.size();
while(iMaterialTextureDepth < 2*int32_t(MaterialTextureMap.size()))
iMaterialTextureDepth <<= 1;
int32_t iNormalDepth = iMaterialTextureDepth / 2; int32_t iNormalDepth = iMaterialTextureDepth / 2;


// Find the largest texture // Find the largest texture
Expand All @@ -186,15 +184,16 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)


// Get size for our textures. We might be limited by hardware // Get size for our textures. We might be limited by hardware
int iTexWdt = pRefSfc->Wdt, iTexHgt = pRefSfc->Hgt; int iTexWdt = pRefSfc->Wdt, iTexHgt = pRefSfc->Hgt;
GLint iMaxTexSize; GLint iMaxTexSize, iMaxTexLayers;
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &iMaxTexSize); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &iMaxTexSize);
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &iMaxTexLayers);
if (iTexWdt > iMaxTexSize || iTexHgt > iMaxTexSize) if (iTexWdt > iMaxTexSize || iTexHgt > iMaxTexSize)
{ {
iTexWdt = Min(iTexWdt, iMaxTexSize); iTexWdt = Min(iTexWdt, iMaxTexSize);
iTexHgt = Min(iTexHgt, iMaxTexSize); iTexHgt = Min(iTexHgt, iMaxTexSize);
LogF(" gl: Material textures too large, GPU only supports %dx%d! Cropping might occur!", iMaxTexSize, iMaxTexSize); LogF(" gl: Material textures too large, GPU only supports %dx%d! Cropping might occur!", iMaxTexSize, iMaxTexSize);
} }
if(iMaterialTextureDepth >= iMaxTexSize) if(iMaterialTextureDepth >= iMaxTexLayers)
{ {
LogF(" gl: Too many material textures! GPU only supports 3D texture depth of %d!", iMaxTexSize); LogF(" gl: Too many material textures! GPU only supports 3D texture depth of %d!", iMaxTexSize);
return false; return false;
Expand Down Expand Up @@ -265,70 +264,42 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
// Clear error error(s?) // Clear error error(s?)
while(glGetError()) {} while(glGetError()) {}


// Alloc 3D textures // Alloc 2D texture array
glEnable(GL_TEXTURE_3D); //glEnable(GL_TEXTURE_2D_ARRAY);
glGenTextures(C4LR_MipMapCount, hMaterialTexture); glGenTextures(1, &hMaterialTexture);

// Generate textures (mipmaps too!) // Generate textures
int iSizeSum = 0; int iSizeSum = 0;
BYTE *pLastData = new BYTE [iSize / 4]; BYTE *pLastData = new BYTE [iSize / 4];
for(int iMMLevel = 0; iMMLevel < C4LR_MipMapCount; iMMLevel++)
{

// Scale the texture down for mip-mapping
if(iMMLevel) {
BYTE *pOut = pData;
BYTE *pIn[4] = {
pLastData, pLastData + iBytesPP,
pLastData + iBytesPP * iTexWdt, pLastData + iBytesPP * iTexWdt + iBytesPP
};
for (int i = 0; i < iMaterialTextureDepth; ++i)
for (int y = 0; y < iTexHgt / 2; ++y)
{
for (int x = 0; x < iTexWdt / 2; ++x)
{
for (int j = 0; j < iBytesPP; j++)
{
unsigned int s = 0;
s += *pIn[0]++; s += 3 * *pIn[1]++; s += 3 * *pIn[2]++; s += *pIn[3]++;
*pOut++ = BYTE(s / 8);
}
pIn[0] += iBytesPP; pIn[1] += iBytesPP; pIn[2] += iBytesPP; pIn[3] += iBytesPP;
}
pIn[0] += iBytesPP * iTexWdt; pIn[1] += iBytesPP * iTexWdt;
pIn[2] += iBytesPP * iTexWdt; pIn[3] += iBytesPP * iTexWdt;
}
iTexWdt /= 2; iTexHgt /= 2;
}


// Select texture // Select texture
glBindTexture(GL_TEXTURE_3D, hMaterialTexture[iMMLevel]); glBindTexture(GL_TEXTURE_2D_ARRAY, hMaterialTexture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);


// We fully expect to tile these // We fully expect to tile these
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);

glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_GENERATE_MIPMAP, GL_TRUE);
// Make it happen!
glTexImage3D(GL_TEXTURE_3D, 0, 4, iTexWdt, iTexHgt, iMaterialTextureDepth, 0, GL_BGRA, // Make it happen!
iBytesPP == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV, glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, 4, iTexWdt, iTexHgt, iMaterialTextureDepth, 0, GL_BGRA,
pData); iBytesPP == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV,
pData);


// Exchange buffers // Exchange buffers
BYTE *tmp = pLastData; BYTE *tmp = pLastData;
pLastData = pData; pLastData = pData;
pData = tmp; pData = tmp;


// Statistics // Statistics
iSizeSum += iTexWdt * iTexHgt * iMaterialTextureDepth * iBytesPP; iSizeSum += iTexWdt * iTexHgt * iMaterialTextureDepth * iBytesPP;
}


// Dispose of data // Dispose of data
delete [] pData; delete [] pData;
delete [] pLastData; delete [] pLastData;
glDisable(GL_TEXTURE_3D); //glDisable(GL_TEXTURE_3D);


// Check whether we were successful // Check whether we were successful
if(int err = glGetError()) if(int err = glGetError())
Expand All @@ -338,10 +309,9 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
} }


// Announce the good news // Announce the good news
LogF(" gl: Texturing uses %d slots at %dx%d, %d levels (%d MB total)", LogF(" gl: Texturing uses %d slots at %dx%d (%d MB total)",
static_cast<int>(MaterialTextureMap.size()), static_cast<int>(MaterialTextureMap.size()),
iMaterialWidth, iMaterialHeight, iMaterialWidth, iMaterialHeight,
C4LR_MipMapCount,
iSizeSum / 1000000); iSizeSum / 1000000);


return true; return true;
Expand Down Expand Up @@ -852,7 +822,7 @@ void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap)
} }


// Assign texture // Assign texture
if(pFMap) pFMap[pix] = (gTexCoo + 0.5) / iMaterialTextureDepth; if(pFMap) pFMap[pix] = gTexCoo / iMaterialTextureDepth;
if(pIMap) pIMap[pix] = int((gTexCoo * 256.0 / iMaterialTextureDepth) + 0.5); if(pIMap) pIMap[pix] = int((gTexCoo * 256.0 / iMaterialTextureDepth) + 0.5);
} }
} }
Expand Down Expand Up @@ -896,7 +866,9 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh
BuildMatMap(MatMap, NULL); BuildMatMap(MatMap, NULL);
ShaderCall.SetUniform1fv(C4LRU_MatMap, 256, MatMap); ShaderCall.SetUniform1fv(C4LRU_MatMap, 256, MatMap);
} }
ShaderCall.SetUniform1i(C4LRU_MaterialDepth, iMaterialTextureDepth);
float fMaterialTextureDepth = iMaterialTextureDepth;
ShaderCall.SetUniform1f(C4LRU_MaterialDepth, fMaterialTextureDepth);
ShaderCall.SetUniform2f(C4LRU_MaterialSize, ShaderCall.SetUniform2f(C4LRU_MaterialSize,
float(iMaterialWidth) / ::Game.C4S.Landscape.MaterialZoom, float(iMaterialWidth) / ::Game.C4S.Landscape.MaterialZoom,
float(iMaterialHeight) / ::Game.C4S.Landscape.MaterialZoom); float(iMaterialHeight) / ::Game.C4S.Landscape.MaterialZoom);
Expand Down Expand Up @@ -951,13 +923,7 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh
} }
if(ShaderCall.AllocTexUnit(C4LRU_MaterialTex)) if(ShaderCall.AllocTexUnit(C4LRU_MaterialTex))
{ {
// Decide which mip-map level to use glBindTexture(GL_TEXTURE_2D_ARRAY, hMaterialTexture);
double z = 0.5; int iMM = 0;
while(pGL->Zoom < z * ::Game.C4S.Landscape.MaterialZoom && iMM + 1 <C4LR_MipMapCount)
{ z /= 2; iMM++; }
glBindTexture(GL_TEXTURE_3D, hMaterialTexture[iMM]);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
} }
if(ShaderCall.AllocTexUnit(C4LRU_MatMapTex)) if(ShaderCall.AllocTexUnit(C4LRU_MatMapTex))
{ {
Expand Down
9 changes: 3 additions & 6 deletions src/landscape/C4LandscapeRender.h
Expand Up @@ -62,9 +62,6 @@ const int C4LR_BytesPerPx = 3;
const int C4LR_BytesPerSurface = 4; const int C4LR_BytesPerSurface = 4;
const int C4LR_SurfaceCount = (C4LR_ByteCount + C4LR_BytesPerSurface - 1) / C4LR_BytesPerSurface; const int C4LR_SurfaceCount = (C4LR_ByteCount + C4LR_BytesPerSurface - 1) / C4LR_BytesPerSurface;


// How many mip-map levels should be used at maximum?
const int C4LR_MipMapCount = 6;

class C4Landscape; class C4TextureMap; class C4Landscape; class C4TextureMap;


class C4LandscapeRender class C4LandscapeRender
Expand Down Expand Up @@ -112,9 +109,9 @@ class C4LandscapeRenderGL : public C4LandscapeRender
static const char *UniformNames[]; static const char *UniformNames[];
GLenum hLandscapeTexCoord, hLightTexCoord; GLenum hLandscapeTexCoord, hLightTexCoord;


// 3D texture of material textures // 2D texture array of material textures
GLuint hMaterialTexture[C4LR_MipMapCount]; GLuint hMaterialTexture;
// material texture positions in 3D texture // material texture positions in texture array
std::vector<StdCopyStrBuf> MaterialTextureMap; std::vector<StdCopyStrBuf> MaterialTextureMap;
// depth of material texture in layers // depth of material texture in layers
int32_t iMaterialTextureDepth; int32_t iMaterialTextureDepth;
Expand Down

0 comments on commit 790219a

Please sign in to comment.