diff --git a/planet/Graphics.ocg/LandscapeShader.c b/planet/Graphics.ocg/LandscapeShader.c index 67812387e..0807bd89b 100644 --- a/planet/Graphics.ocg/LandscapeShader.c +++ b/planet/Graphics.ocg/LandscapeShader.c @@ -10,7 +10,9 @@ uniform sampler3D materialTex; uniform vec2 resolution; // Texture map -uniform float matTexMap[256]; +uniform float matMap[256]; +uniform sampler1D matMapTex; +uniform int materialDepth; // Expected parameters for the scaler const vec2 scalerStepX = vec2(1.0 / 8.0, 0.0); @@ -18,6 +20,15 @@ const vec2 scalerStepY = vec2(0.0, 1.0 / 32.0); const vec2 scalerOffset = vec2(0.0, 0.0) + scalerStepX / 3.0 + scalerStepY / 3.0; const vec2 scalerPixel = vec2(scalerStepX.x, scalerStepY.y) / 3.0; +float queryMatMap(int pix) +{ +#ifdef BROKEN_ARRAYS_WORKAROUND + return texture1D(matMapTex, float(pix) / 256.0 + 0.5 / 256.0).r * 255.0 / float(materialDepth) + 0.5 / float(materialDepth); +#else + return matMap[pix]; +#endif +} + void main() { // full pixel steps in the landscape texture (depends on landscape resolution) @@ -59,13 +70,13 @@ void main() if(texture2D(landscapeTex[0], centerCoo + fullStepX + fullStepY).r == lpx.r) scalerCoo += 16.0 * scalerStepY; - vec4 spx = texture2D(scalerTex, scalerCoo, 0.0); + vec4 spx = texture2D(scalerTex, scalerCoo); - // Material pixel - float mix = matTexMap[int(lpx.r * 255.0)]; - vec4 mpx = texture3D(materialTex, vec3(gl_TexCoord[0].st * resolution / vec2(512.0, 512.0) * vec2(3.0, 3.0), mix)); - float omix = matTexMap[int(lopx.r * 255.0)]; - vec4 ompx = texture3D(materialTex, vec3(gl_TexCoord[0].st * resolution / vec2(512.0, 512.0) * vec2(3.0, 3.0), omix)); + // Get material pixels + float mi = queryMatMap(int(lpx.r * 255.0)); + vec4 mpx = texture3D(materialTex, vec3(gl_TexCoord[0].st * resolution / vec2(512.0, 512.0) * vec2(3.0, 3.0), mi)); + float omi = queryMatMap(int(lopx.r * 255.0)); + vec4 ompx = texture3D(materialTex, vec3(gl_TexCoord[0].st * resolution / vec2(512.0, 512.0) * vec2(3.0, 3.0), omi)); // Brightness vec2 normal = (1.5 * rlpx.yz - vec2(1.0, 1.0)); @@ -73,7 +84,8 @@ void main() float bright = ambientBright * (1.0 + dot(normal, vec2(0.0, -1.0))); float bright2 = ambientBright; - gl_FragColor = vec4( - bright * spx.r * mpx.rgb + bright2 * (1.0-spx.r) * ompx.rgb, - spx.r * mpx.a + (1.0-spx.r) * ompx.a); + gl_FragColor = mix( + vec4(bright2 * ompx.rgb, ompx.a), + vec4(bright * mpx.rgb, mpx.a), + spx.r); } diff --git a/src/game/landscape/C4LandscapeRender.cpp b/src/game/landscape/C4LandscapeRender.cpp index e493ff0e9..f4119efe7 100644 --- a/src/game/landscape/C4LandscapeRender.cpp +++ b/src/game/landscape/C4LandscapeRender.cpp @@ -16,20 +16,53 @@ // Automatically reload shaders when changed at runtime? #define AUTO_RELOAD_SHADERS +// 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 + +#endif + // How much to look into each direction for bias const int C4LR_BiasDistanceX = 8; const int C4LR_BiasDistanceY = 8; +// Workarounds to try if shader fails to compile +const char *C4LR_ShaderWorkarounds[] = { + "", + "#define BROKEN_ARRAYS_WORKAROUND\n", +}; +const int C4LR_ShaderWorkaroundCount = sizeof(C4LR_ShaderWorkarounds) / sizeof(*C4LR_ShaderWorkarounds); + +// Map of uniforms to names in shader +static const char *GetUniformName(int iUniform) +{ + switch(iUniform) + { + case C4LRU_LandscapeTex: return "landscapeTex"; + case C4LRU_ScalerTex: return "scalerTex"; + case C4LRU_MaterialTex: return "materialTex"; + case C4LRU_Resolution: return "resolution"; + case C4LRU_MatMap: return "matMap"; + case C4LRU_MatMapTex: return "matMapTex"; + case C4LRU_MaterialDepth:return "materialDepth"; + } + assert(false); + return "mysterious"; +} + C4LandscapeRenderGL::C4LandscapeRenderGL() : iLandscapeShaderTime(0), hVert(0), hFrag(0), hProg(0), - hLandscapeUnit(0), hScalerUnit(0), hMaterialUnit(0), - hResolutionUniform(0), hMatTexMapUniform(0), iTexCount(0) { - ZeroMem(MatTexMap, sizeof(MatTexMap)); ZeroMem(Surfaces, sizeof(Surfaces)); ZeroMem(hMaterialTexture, sizeof(hMaterialTexture)); + ZeroMem(hUniforms, sizeof(hUniforms)); } C4LandscapeRenderGL::~C4LandscapeRenderGL() @@ -55,6 +88,7 @@ bool C4LandscapeRenderGL::Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pT // Safe info this->iWidth = iWidth; this->iHeight = iHeight; + this->pTexs = pTexs; // Count the textures iTexCount = 0; @@ -82,23 +116,6 @@ bool C4LandscapeRenderGL::Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pT return false; } - // Build material-texture map (depth parameter where to find appropriate texture) - for(int pix = 0; pix < 256; pix++) - { - // Look up indexed entry - const C4TexMapEntry *pEntry = pTexs->GetEntry(PixCol2Tex(BYTE(pix))); - if(!pEntry->GetTextureName()) - { - // Textures over iTexCount are transparent - MatTexMap[pix] = (float(iTexCount) + 0.5) / iMaterialTextureDepth; - continue; - } - // Assign texture - int32_t iTexIndex = pTexs->GetTextureIndex(pEntry->GetTextureName()); - if(iTexIndex < 0) iTexIndex = 0; - MatTexMap[pix] = (float(iTexIndex) + 0.5) / iMaterialTextureDepth; - } - return true; } @@ -126,12 +143,16 @@ 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. Lowest power of 2 that can contain - // all textures plus sky (= empty). Might have more complicated + // Determine depth to use. Might have more complicated // mappings in future. iMaterialTextureDepth = 1; - while(iMaterialTextureDepth < iTexCount + 1) + while(iMaterialTextureDepth < iTexCountP + 1) iMaterialTextureDepth <<= 1; // Find first (actual) texture @@ -151,8 +172,29 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs) for(int i = 0; i < iMaterialTextureDepth; i++) { BYTE *p = pData + i * iTexSize; +#ifdef DEBUG_SEPERATOR_TEXTURES + if (i % 2) + { + // Make every second texture ugly stripes + DWORD *texdata = reinterpret_cast(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(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(i)))) + if(!(pTex = pTexs->GetTexture(pTexs->GetTexture(iTex)))) {} else if(!(pSurface = pTex->Surface32)) {} @@ -162,7 +204,7 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs) { // Size recheck if(pSurface->Wdt != iTexWdt || pSurface->Hgt != iTexHgt) - LogF(" gl: texture %s size mismatch (%dx%d vs %dx%d)!", pTexs->GetTexture(i), pSurface->Wdt, pSurface->Hgt, iTexWdt, 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(p); pSurface->Lock(); @@ -198,7 +240,7 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs) pLastData, pLastData + iBytesPP, pLastData + iBytesPP * iTexWdt, pLastData + iBytesPP * iTexWdt + iBytesPP }; - for (int i = 0; i <= iTexCount; ++i) + for (int i = 0; i <= iTexCountP; ++i) for (int y = 0; y < iTexHgt / 2; ++y) { for (int x = 0; x < iTexWdt / 2; ++x) @@ -391,19 +433,24 @@ int C4LandscapeRenderGL::GetObjectStatus(GLhandleARB hObj, GLenum type) GLhandleARB C4LandscapeRenderGL::CreateShader(GLenum iShaderType, const char *szWhat, const char *szCode) { - const char *szCodes[1] = { szCode }; - GLhandleARB hShader = glCreateShaderObjectARB(iShaderType); - glShaderSourceARB(hShader, 1, szCodes, 0); - glCompileShaderARB(hShader); + // Try all workarounds until one works + for(int iWorkaround = 0; iWorkaround < C4LR_ShaderWorkaroundCount; iWorkaround++) + { + // Build code + const char *szCodes[2] = { C4LR_ShaderWorkarounds[iWorkaround], szCode }; + GLhandleARB hShader = glCreateShaderObjectARB(iShaderType); + glShaderSourceARB(hShader, 2, szCodes, 0); + glCompileShaderARB(hShader); - // Dump any information to log - DumpInfoLog(szWhat, hShader); + // Dump any information to log + DumpInfoLog(szWhat, hShader); - // Success? - if(GetObjectStatus(hShader, GL_OBJECT_COMPILE_STATUS_ARB) != 1) - return 0; - else - return hShader; + // Success? + if(GetObjectStatus(hShader, GL_OBJECT_COMPILE_STATUS_ARB) == 1) + return hShader; + } + // Did not work :/ + return 0; } bool C4LandscapeRenderGL::InitShaders() @@ -439,12 +486,10 @@ bool C4LandscapeRenderGL::InitShaders() return false; } - // Get variable locations - hLandscapeUnit = glGetUniformLocationARB(hProg, "landscapeTex"); - hScalerUnit = glGetUniformLocationARB(hProg, "scalerTex"); - hMaterialUnit = glGetUniformLocationARB(hProg, "materialTex"); - hResolutionUniform = glGetUniformLocationARB(hProg, "resolution"); - hMatTexMapUniform = glGetUniformLocationARB(hProg, "matTexMap"); + // Get uniform locations. Note this is expected to fail for a few of them + // because the respective uniforms got optimized out! + for (int i = 0; i < C4LRU_Count; i++) + hUniforms[i] = glGetUniformLocationARB(hProg, GetUniformName(i)); // Success? if(int err = glGetError()) @@ -467,8 +512,7 @@ void C4LandscapeRenderGL::ClearShaders() glDeleteObjectARB(hProg); hFrag = hVert = hProg = 0; - hLandscapeUnit = hScalerUnit = hMaterialUnit = 0; - hResolutionUniform = hMatTexMapUniform = 0; + ZeroMem(hUniforms, sizeof(hUniforms)); } bool C4LandscapeRenderGL::LoadShaders(C4GroupSet *pGroups) @@ -500,16 +544,8 @@ bool C4LandscapeRenderGL::LoadScaler(C4GroupSet *pGroups) return fctScaler.Load(*pGroup, C4CFN_LandscapeScaler); } -void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo) +void C4LandscapeRenderGL::RefreshShaders() { - // Must have GL and be initialized - if(!pGL && !hProg) return; - - // prepare rendering to surface - CSurface *sfcTarget = cgo.Surface; - if (!pGL->PrepareRendering(sfcTarget)) return; - -#ifdef AUTO_RELOAD_SHADERS // File changed? if(!LandscapeShaderPath.isNull() && FileTime(LandscapeShaderPath.getData()) != iLandscapeShaderTime) @@ -526,6 +562,46 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo) InitShaders(); iLandscapeShaderTime = FileTime(LandscapeShaderPath.getData()); } +} + +void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap) +{ + // TODO: Still merely an inefficient placeholder for things to come... + + // Build material-texture map (depth parameter where to find appropriate texture) + for(int pix = 0; pix < 256; pix++) + { + // Look up indexed entry + 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; + continue; + } + // 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; + } +} + +void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo) +{ + // Must have GL and be initialized + if(!pGL && !hProg) return; + + // prepare rendering to surface + CSurface *sfcTarget = cgo.Surface; + if (!pGL->PrepareRendering(sfcTarget)) return; + +#ifdef AUTO_RELOAD_SHADERS + RefreshShaders(); #endif // AUTO_RELOAD_SHADERS // Clear error(s?) @@ -535,42 +611,55 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo) glUseProgramObjectARB(hProg); // Bind data - glUniform1fvARB(hMatTexMapUniform, 256, MatTexMap); - glUniform2fARB(hResolutionUniform, iWidth, iHeight); - - // Bind textures - int iUnit = 0; int iMaterialUnit = -1; - if(hScalerUnit >= 0) + if (hUniforms[C4LRU_Resolution] != -1) + glUniform2fARB(hUniforms[C4LRU_Resolution], iWidth, iHeight); + if (hUniforms[C4LRU_MatMap] != -1) { - glUniform1iARB(hScalerUnit, iUnit); - glActiveTexture(GL_TEXTURE0 + iUnit); - iUnit++; - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, fctScaler.Surface->ppTex[0]->texName); + GLfloat MatMap[256]; + BuildMatMap(MatMap, NULL); + glUniform1fvARB(hUniforms[C4LRU_MatMap], 256, MatMap); } - if(hMaterialUnit >= 0) + if (hUniforms[C4LRU_MaterialDepth] != -1) + glUniform1iARB(hUniforms[C4LRU_MaterialDepth], iMaterialTextureDepth); + + // Setup facilities for texture unit allocation (gimme local functions...) + int iUnit = 0; int iUnitMap[32]; ZeroMem(iUnitMap, sizeof(iUnitMap)); + #define ALLOC_UNIT(hUniform, iType) do { \ + if(hUniform != -1) glUniform1iARB(hUniform, iUnit); \ + glActiveTexture(GL_TEXTURE0 + iUnit); \ + iUnitMap[iUnit] = iType; \ + glEnable(iType); \ + iUnit++; \ + assert(iUnit < 32); \ + } while(false) + + // Start binding textures + if(hUniforms[C4LRU_ScalerTex] != -1) { - iMaterialUnit = iUnit; - glUniform1iARB(hMaterialUnit, iUnit); - glActiveTexture(GL_TEXTURE0 + iUnit); - iUnit++; - glEnable(GL_TEXTURE_3D); + ALLOC_UNIT(hUniforms[C4LRU_ScalerTex], GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, fctScaler.Surface->ppTex[0]->texName); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + if(hUniforms[C4LRU_MaterialTex] != -1) + { + ALLOC_UNIT(hUniforms[C4LRU_MaterialTex], GL_TEXTURE_3D); // Decide which mip-map level to use - double z = 2.0; int iMM = 0; + double z = 1.5; int iMM = 0; while(pGL->Zoom < z && iMM + 1 = 0) + if(hUniforms[C4LRU_LandscapeTex] != -1) { GLint iLandscapeUnits[C4LR_SurfaceCount]; for(int i = 0; i < C4LR_SurfaceCount; i++) { iLandscapeUnits[i] = iUnit; - glActiveTexture(GL_TEXTURE0 + iUnit); - iUnit++; - glEnable(GL_TEXTURE_2D); + ALLOC_UNIT(-1, GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, Surfaces[i]->ppTex[0]->texName); if (pGL->Zoom != 1.0) { @@ -583,7 +672,15 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } } - glUniform1ivARB(hLandscapeUnit, C4LR_SurfaceCount, iLandscapeUnits); + glUniform1ivARB(hUniforms[C4LRU_LandscapeTex], C4LR_SurfaceCount, iLandscapeUnits); + } + if(hUniforms[C4LRU_MatMapTex] != -1) + { + ALLOC_UNIT(hUniforms[C4LRU_MatMapTex], GL_TEXTURE_1D); + GLubyte MatMap[256]; + BuildMatMap(NULL, MatMap); + glTexImage1D(GL_TEXTURE_1D, 0, 1, 256, 0, GL_RED, GL_UNSIGNED_BYTE, MatMap); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } // set up blit data as rect @@ -631,7 +728,6 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo) // Blend it glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); // FIXME: needed? // Blit glInterleavedArrays(GL_T2F_C4UB_V3F, sizeof(CBltVertex), Vtx); @@ -645,13 +741,13 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo) { iUnit--; glActiveTexture(GL_TEXTURE0 + iUnit); - glDisable(iUnit == iMaterialUnit ? GL_TEXTURE_3D : GL_TEXTURE_2D); + glDisable(iUnitMap[iUnit]); } // Got an error? if(int err = glGetError()) { - LogF("GL error: %d", err); + LogF("GL error: %d", err /*, gluErrorString(err)*/); } } diff --git a/src/game/landscape/C4LandscapeRender.h b/src/game/landscape/C4LandscapeRender.h index 00603bf52..3751e9f5d 100644 --- a/src/game/landscape/C4LandscapeRender.h +++ b/src/game/landscape/C4LandscapeRender.h @@ -14,6 +14,22 @@ enum C4LR_Byte { C4LR_ByteCount }; +// Uniform data we give the shader (constants from its viewpoint) +// Don't forget to update GetUniformName when introducing new uniforms! +enum C4LR_Uniforms +{ + C4LRU_LandscapeTex, + C4LRU_ScalerTex, + C4LRU_MaterialTex, + + C4LRU_Resolution, + C4LRU_MatMap, + C4LRU_MatMapTex, + C4LRU_MaterialDepth, + + C4LRU_Count +}; + // How much data we want to store per landscape pixel const int C4LR_BytesPerPx = 3; @@ -71,8 +87,7 @@ private: // shaders GLhandleARB hVert, hFrag, hProg; // shader variables - GLhandleARB hLandscapeUnit, hScalerUnit, hMaterialUnit; - GLhandleARB hResolutionUniform, hMatTexMapUniform; + GLhandleARB hUniforms[C4LRU_Count]; // Texture count int32_t iTexCount; @@ -80,8 +95,6 @@ private: GLuint hMaterialTexture[C4LR_MipMapCount]; // depth of material texture in layers int32_t iMaterialTextureDepth; - // material map - GLfloat MatTexMap[256]; // scaler image C4FacetSurface fctScaler; @@ -95,6 +108,8 @@ public: virtual void Draw(const C4TargetFacet &cgo); + void RefreshShaders(); + private: bool InitMaterialTexture(C4TextureMap *pMap); bool LoadShaders(C4GroupSet *pGraphics); @@ -106,6 +121,8 @@ private: bool InitShaders(); void ClearShaders(); + + void BuildMatMap(GLfloat *pFMap, GLubyte *pIMap); }; #endif