Modular shader system

The idea here is that we compose shaders out of "slices", which can
come from the engine ("built-in"), from files or possibly even from
models. This should allow us to more easily share the code between
different rendering shaders (e.g. for lights / normals).

TODO: Workarounds not yet implemented, so this might degrade less
gracefully.
issue1247
Peter Wortmann 2014-11-20 11:52:14 +01:00
parent b43e1a9369
commit 72289713f9
10 changed files with 930 additions and 437 deletions

View File

@ -216,6 +216,8 @@ set(OC_CLONK_SOURCES
src/graphics/C4FontLoader.h
src/graphics/C4GraphicsResource.cpp
src/graphics/C4GraphicsResource.h
src/graphics/C4Shader.cpp
src/graphics/C4Shader.h
src/graphics/C4Surface.cpp
src/graphics/C4Surface.h
src/graphics/C4SurfaceLoaders.cpp

View File

@ -0,0 +1,23 @@
// Ambient light calculation
uniform sampler2D ambientTex;
// Factor between landscape coordinates and ambient map coordinates
uniform vec2 ambientScale;
slice texture+6
{
// Ambient light
float ambient = texture2D(ambientTex, ambientScale * texCoo).r;
}
slice light+1
{
// Add ambience to brightness
vec3 ambientDir = vec3(0.0, -1.0, 0.0);
light = mix(light, 1.0 + 1.0 * dot(normal, ambientDir), ambient);
#ifdef HAVE_2PX
light2 = mix(light2, 1.0 + 1.0 * dot(normal2, ambientDir), ambient);
#endif
}

View File

@ -1,10 +1,7 @@
#version 110
// Input textures
uniform sampler2D landscapeTex[2];
uniform sampler2D lightTex;
uniform sampler2D ambientTex;
uniform sampler2D scalerTex;
uniform sampler3D materialTex;
@ -22,25 +19,12 @@ uniform float matMap[256];
uniform int materialDepth;
uniform vec2 materialSize;
// Factor between landscape coordinates and ambient map coordinates
uniform vec2 ambientScale;
// Expected parameters for the scaler
const vec2 scalerStepX = vec2(1.0 / 8.0, 0.0);
const vec2 scalerStepY = vec2(0.0, 1.0 / 32.0);
const vec2 scalerOffset = scalerStepX / 3.0 + scalerStepY / 3.0;
const vec2 scalerPixel = vec2(scalerStepX.x, scalerStepY.y) / 3.0;
#ifdef NO_TEXTURE_LOD_IN_FRAGMENT
#define texture1DLod(t,c,l) texture1D(t,c)
#define texture2DLod(t,c,l) texture2D(t,c)
#endif
// Converts the pixel range 0.0..1.0 into the integer range 0..255
int f2i(float x) {
return int(x * 255.9);
}
float queryMatMap(int pix)
{
#ifndef NO_BROKEN_ARRAYS_WORKAROUND
@ -51,111 +35,64 @@ float queryMatMap(int pix)
#endif
}
vec3 extend_normal(vec2 v)
slice coordinate
{
return normalize(vec3(v, 0.3));
}
void main()
{
// full pixel steps in the landscape texture (depends on landscape resolution)
vec2 fullStep = vec2(1.0, 1.0) / resolution;
vec2 fullStepX = vec2(fullStep.x, 0.0);
vec2 fullStepY = vec2(0.0, fullStep.y);
vec2 texCoo = gl_TexCoord[0].st;
// calculate pixel position in landscape, find center of current pixel
vec2 pixelCoo = texCoo * resolution;
vec2 centerCoo = (floor(pixelCoo) + vec2(0.5, 0.5)) / resolution;
// Texture coordinate for material
vec2 materialCoo = texCoo * resolution / materialSize;
}
slice texture
{
// our pixel color (without/with interpolation)
vec4 landscapePx = texture2D(landscapeTex[0], centerCoo);
vec4 realLandscapePx = texture2D(landscapeTex[0], texCoo);
}
// find scaler coordinate
vec2 scalerCoo = scalerOffset + mod(pixelCoo, vec2(1.0, 1.0)) * scalerPixel;
#ifdef SCALER_IN_GPU
if(texture2D(landscapeTex[0], centerCoo - fullStepX - fullStepY).r == landscapePx.r)
scalerCoo += scalerStepX;
if(texture2D(landscapeTex[0], centerCoo - fullStepY).r == landscapePx.r)
scalerCoo += 2.0 * scalerStepX;
if(texture2D(landscapeTex[0], centerCoo + fullStepX - fullStepY).r == landscapePx.r)
scalerCoo += 4.0 * scalerStepX;
if(texture2D(landscapeTex[0], centerCoo - fullStepX ).r == landscapePx.r)
scalerCoo += scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepX ).r == landscapePx.r)
scalerCoo += 2.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo - fullStepX + fullStepY).r == landscapePx.r)
scalerCoo += 4.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepY).r == landscapePx.r)
scalerCoo += 8.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepX + fullStepY).r == landscapePx.r)
scalerCoo += 16.0 * scalerStepY;
#else
int iScaler = f2i(landscapePx.a), iRow = iScaler / 8;
scalerCoo.x += float(iScaler - iRow * 8) / 8.0;
scalerCoo.y += float(iScaler / 8) / 32.0;
#endif
// Note: scalerCoo will jump around a lot, causing some GPUs to apparantly get confused with
// the level-of-detail calculation. We therefore try to disable LOD.
vec4 scalerPx = texture2DLod(scalerTex, scalerCoo, 0.0);
// gen3 other coordinate calculation. Still struggles a bit with 3-ways
vec2 otherCoo = centerCoo + fullStep * floor(vec2(-0.5, -0.5) + scalerPx.gb * 255.0 / 64.0);
vec4 otherLandscapePx = texture2D(landscapeTex[0], otherCoo);
slice material
{
// Get material pixels
float materialIx = queryMatMap(f2i(landscapePx.r));
vec2 tcoo = texCoo * resolution / materialSize;
vec4 materialPx = texture3D(materialTex, vec3(tcoo, materialIx));
vec4 normalPx = texture3D(materialTex, vec3(tcoo, materialIx+0.5));
float otherMaterialIx = queryMatMap(f2i(otherLandscapePx.r));
vec4 otherMaterialPx = texture3D(materialTex, vec3(tcoo, otherMaterialIx));
vec4 materialPx = texture3D(materialTex, vec3(materialCoo, materialIx));
vec4 normalPx = texture3D(materialTex, vec3(materialCoo, materialIx+0.5));
// Brightness
vec4 lightPx = texture2D(lightTex, gl_TexCoord[1].st);
float shadeBright = lightPx.r;
// Normal calculation
vec3 landscapeNormal = extend_normal(mix(realLandscapePx.yz, landscapePx.yz, scalerPx.a) - vec2(0.5, 0.5));
vec3 landscapeNormal2 = extend_normal(otherLandscapePx.yz - vec2(0.5, 0.5));
vec3 textureNormal = normalPx.xyz - vec3(0.5,0.5,0.5);
vec3 normal = landscapeNormal + textureNormal;
vec3 normal2 = landscapeNormal2 + textureNormal;
// Ambient light
float ambient = texture2D(ambientTex, ambientScale * texCoo).r;
float ambient2 = texture2D(ambientTex, ambientScale * otherCoo).r;
// Light calculation; the ambient part actually uses some shading with
// a light direction of (0.0, -1.0, 0.0) so that it does not look utterly
// boring at the sky/material edges.
vec3 lightDir = extend_normal(vec2(1.0, 1.0) - lightPx.yz * 3.0);
float bright = ambient * (1.0 + 1.0 * dot(normal, vec3(0.0, -1.0, 0.0))) + (1.0 - ambient) * 2.0 * shadeBright * dot(normal, lightDir);
float bright2 = ambient2 * (1.0 + 1.0 * dot(normal2, vec3(0.0, -1.0, 0.0))) + (1.0 - ambient2) * 2.0 * shadeBright * dot(normal2, lightDir);
gl_FragColor = mix(
vec4(bright2 * otherMaterialPx.rgb, otherMaterialPx.a),
vec4(bright * materialPx.rgb, materialPx.a),
scalerPx.r);
// uncomment the following lines for debugging light directions:
// yellow: light up, blue: light down, turqoise: light right, pink: light left, opacity: light strength
//float lightYDir = lightPx.b - 1.0/3.0;
//float lightXDir = lightPx.g - 1.0/3.0;
//float lightStrength = lightPx.r;
//gl_FragColor = vec4(
// 1.0-1.5*(max(0.0, lightYDir) + max(0.0,lightXDir)),
// 1.0-1.5*(max(0.0, lightYDir) + max(0.0,-lightXDir)),
// 1.0-1.5*max(0.0, -lightYDir),
// lightStrength);
// Same for second pixel, but we'll simply use the first normal
#ifdef HAVE_2PX
float materialIx2 = queryMatMap(f2i(landscapePx2.r));
vec4 materialPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2));
#endif
}
slice normal
{
// Normal calculation
vec3 normal = extend_normal(mix(realLandscapePx.yz, landscapePx.yz, scalerPx.a)
- vec2(0.5, 0.5));
vec3 textureNormal = normalPx.xyz - vec3(0.5,0.5,0.5);
normal = normal + textureNormal;
#ifdef HAVE_2PX
vec3 normal2 = extend_normal(landscapePx2.yz - vec2(0.5, 0.5));
normal2 = normal2 + textureNormal;
#endif
}
slice color {
#define color gl_FragColor
color = materialPx;
#ifdef HAVE_2PX
vec4 color2 = materialPx2;
#endif
}

View File

@ -0,0 +1,51 @@
// Base light calculations
uniform sampler2D lightTex;
// uncomment the following lines for debugging light directions:
// yellow: light up, blue: light down, turqoise: light right, pink: light left
// brightness: light strength
//#define LIGHT_DEBUG
slice texture+5
{
// Query light texture
vec4 lightPx = texture2D(lightTex, lightCoord.st);
float lightBright = lightPx.x;
vec3 lightDir = extend_normal(vec2(1.0, 1.0) - lightPx.yz * 3.0);
}
slice light
{
// Light direction
float light = 2.0 * lightBright * dot(normal, lightDir);
#ifdef HAVE_2PX
float light2 = 2.0 * lightBright * dot(normal2, lightDir);
#endif
}
slice color+5
{
// Add light
color = vec4(light * color.rgb, color.a);
#ifdef HAVE_2PX
color2 = vec4(light2 * color2.rgb, color2.a);
#endif
}
slice finish+5
{
#ifdef LIGHT_DEBUG
float lightYDir = lightPx.b - 1.0/3.0;
float lightXDir = lightPx.g - 1.0/3.0;
float lightStrength = lightPx.r;
gl_FragColor =
vec4(lightStrength * vec3(1.0-1.5*(max(0.0, lightYDir) + max(0.0,lightXDir)),
1.0-1.5*(max(0.0, lightYDir) + max(0.0,-lightXDir)),
1.0-1.5*max(0.0, -lightYDir)),
1.0);
#endif
}

View File

@ -0,0 +1,49 @@
#define HAVE_2PX
slice texture+5
{
// find scaler coordinate
vec2 scalerCoo = scalerOffset + mod(pixelCoo, vec2(1.0, 1.0)) * scalerPixel;
#ifdef SCALER_IN_GPU
if(texture2D(landscapeTex[0], centerCoo - fullStepX - fullStepY).r == landscapePx.r)
scalerCoo += scalerStepX;
if(texture2D(landscapeTex[0], centerCoo - fullStepY).r == landscapePx.r)
scalerCoo += 2.0 * scalerStepX;
if(texture2D(landscapeTex[0], centerCoo + fullStepX - fullStepY).r == landscapePx.r)
scalerCoo += 4.0 * scalerStepX;
if(texture2D(landscapeTex[0], centerCoo - fullStepX ).r == landscapePx.r)
scalerCoo += scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepX ).r == landscapePx.r)
scalerCoo += 2.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo - fullStepX + fullStepY).r == landscapePx.r)
scalerCoo += 4.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepY).r == landscapePx.r)
scalerCoo += 8.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepX + fullStepY).r == landscapePx.r)
scalerCoo += 16.0 * scalerStepY;
#else
int iScaler = f2i(landscapePx.a), iRow = iScaler / 8;
scalerCoo.x += float(iScaler - iRow * 8) / 8.0;
scalerCoo.y += float(iScaler / 8) / 32.0;
#endif
// Note: scalerCoo will jump around a lot, causing some GPUs to
// apparantly get confused with the level-of-detail
// calculation. We therefore try to disable LOD.
vec4 scalerPx = texture2DLod(scalerTex, scalerCoo, 0.0);
// gen3 other coordinate calculation. Still struggles a bit with 3-ways
vec2 centerCoo2 = centerCoo + fullStep * floor(vec2(-0.5, -0.5) +
scalerPx.gb * 255.0 / 64.0);
vec4 landscapePx2 = texture2D(landscapeTex[0], centerCoo2);
}
slice color+5 {
// Mix second color into main color according to scaler
color = mix(color2, color, scalerPx.r);
}

View File

@ -0,0 +1,16 @@
// #ifdef NO_TEXTURE_LOD_IN_FRAGMENT
#define texture1DLod(t,c,l) texture1D(t,c)
#define texture2DLod(t,c,l) texture2D(t,c)
// #endif
vec3 extend_normal(vec2 v)
{
return normalize(vec3(v, 0.3));
}
// Converts the pixel range 0.0..1.0 into the integer range 0..255
int f2i(float x) {
return int(x * 255.9);
}

View File

@ -0,0 +1,521 @@
#include "C4Include.h"
#include "C4Shader.h"
struct C4ShaderPosName {
int Position; const char *Name;
};
C4ShaderPosName C4SH_PosNames[] = {
{ C4Shader_PositionInit, "init" },
{ C4Shader_PositionCoordinate, "coordinate" },
{ C4Shader_PositionTexture, "texture" },
{ C4Shader_PositionMaterial, "material" },
{ C4Shader_PositionNormal, "normal" },
{ C4Shader_PositionLight, "light" },
{ C4Shader_PositionColor, "color" },
{ C4Shader_PositionFinish, "finish" },
};
C4Shader::C4Shader()
: iTexCoords(0)
, hVert(0), hFrag(0), hProg(0)
, pUniforms(NULL)
{
}
C4Shader::~C4Shader()
{
Clear();
}
void C4Shader::AddVertexSlice(int iPos, const char *szText)
{
ShaderSlice Slice;
Slice.Position = iPos;
Slice.Text.Copy(szText);
Slice.SourceTime = 0;
VertexSlices.push_back(Slice);
}
void C4Shader::AddFragmentSlice(int iPos, const char *szText, const char *szSource, int iSourceTime)
{
ShaderSlice Slice;
Slice.Position = iPos;
Slice.Text.Copy(szText);
Slice.Source = szSource;
Slice.SourceTime = iSourceTime;
FragmentSlices.push_back(Slice);
}
void C4Shader::AddSlices(const char *szWhat, const char *szText, const char *szSource, int iSourceTime)
{
const char *pStart = szText, *pPos = szText;
int iDepth = -1;
int iPosition = -1;
bool fGotContent = false; // Anything in the slice apart from comments and white-space?
// Find slices
while(*pPos) {
// Comment? Might seem silly, but we don't want to get confused by braces in comments...
if (*pPos == '/' && *(pPos + 1) == '/') {
pPos += 2;
while (*pPos && *pPos != '\n') pPos++;
continue;
}
if (*pPos == '/' && *(pPos + 1) == '*') {
pPos += 2;
while (*pPos && (*pPos != '*' || *(pPos+1) != '/')) pPos++;
if (*pPos) pPos += 2;
continue;
}
// Opening brace?
if (*pPos == '{') {
iDepth++; pPos++;
continue;
}
if (*pPos == '}') {
// End of slice?
if (iPosition != -1 && !iDepth) {
// Have a new slice!
if (fGotContent)
{
StdStrBuf Str; Str.Copy(pStart, pPos - pStart);
AddFragmentSlice(iPosition, Str.getData(), szSource, iSourceTime);
}
iPosition = -1;
pStart = pPos+1;
fGotContent = false;
}
if (iDepth >= 0)
iDepth--;
pPos++;
continue;
}
// New slice? We need a newline followed by "slice"
if (iDepth < 0 && isspace(*pPos)) {
if (SEqual2(pPos+1, "slice") && isspace(*(pPos+6))) {
const char *pSliceEnd = pPos; pPos += 6;
// Now let's parse the position
iPosition = ParsePosition(szWhat, &pPos);
if (iPosition != -1) {
// Make sure an opening brace follows
while(isspace(*pPos)) pPos++;
if (*pPos == '{') {
// Add code before "slice" as new slice
if (fGotContent)
{
StdStrBuf Str; Str.Copy(pStart, pSliceEnd - pStart);
AddFragmentSlice(-1, Str.getData(), szSource, iSourceTime);
}
iDepth = 0;
pStart = pPos+1;
fGotContent = false;
} else {
LogF(" gl: Missing opening brace in %s!", szWhat);
}
pPos++;
continue;
}
}
}
// Otherwise: Continue
if (!isspace(*pPos)) fGotContent = true;
pPos++;
}
// Add final slice
if (fGotContent)
{
StdStrBuf Str; Str.Copy(pStart, pPos - pStart);
AddFragmentSlice(iPosition, Str.getData(), szSource, iSourceTime);
}
}
int C4Shader::ParsePosition(const char *szWhat, const char **ppPos)
{
const char *pPos = *ppPos;
while (isspace(*pPos)) pPos++;
// Expect a name
const char *pStart = pPos;
while (isalnum(*pPos)) pPos++;
StdStrBuf Name; Name.Copy(pStart, pPos - pStart);
// Lookup name
int iPosition = -1;
for (int i = 0; i < sizeof(C4SH_PosNames) / sizeof(*C4SH_PosNames); i++) {
if (SEqual(Name.getData(), C4SH_PosNames[i].Name)) {
iPosition = C4SH_PosNames[i].Position;
break;
}
}
if (iPosition == -1) {
LogF(" gl: Unknown slice position in %s: %s", szWhat, Name.getData());
return -1;
}
// Add modifier
while (isspace(*pPos)) pPos++;
if (*pPos == '+') {
int iMod, iModLen;
if (!sscanf(pPos+1, "%d%n", &iMod, &iModLen)) {
LogF(" gl: Invalid slice modifier in %s", szWhat);
return -1;
}
iPosition += iMod;
pPos += 1+iModLen;
}
if (*pPos == '-') {
int iMod, iModLen;
if (!sscanf(pPos+1, "%d%n", &iMod, &iModLen)) {
LogF(" gl: Invalid slice modifier in %s", szWhat);
return -1;
}
iPosition -= iMod;
pPos += 1+iModLen;
}
// Everything okay!
*ppPos = pPos;
return iPosition;
}
bool C4Shader::LoadSlices(C4GroupSet *pGroups, const char *szFile)
{
// Search for our shaders
C4Group *pGroup = pGroups->FindEntry(szFile);
if(!pGroup) return false;
// Load it, save the path for later reloading
StdStrBuf Shader;
if(!pGroup->LoadEntryString(szFile, &Shader))
return false;
// If it physically exists, save back creation time so we
// can automatically reload it if it changes
StdStrBuf Source = FormatString("%s" DirSep "%s", pGroup->GetFullName().getData(), szFile);
int iSourceTime = 0;
if(FileExists(Source.getData()))
iSourceTime = FileTime(Source.getData());
// Load
StdStrBuf What = FormatString("file %s", Config.AtRelativePath(Source.getData()));
AddSlices(What.getData(), Shader.getData(), Source.getData(), iSourceTime);
return true;
}
void C4Shader::AddVertexDefaults()
{
AddVertexSlice(C4Shader_Vertex_PositionPos, "gl_Position = ftransform();\n");
}
GLenum C4Shader::AddTexCoord(const char *szName)
{
// Make sure we have enough space
assert(iTexCoords < C4Shader_MaxTexCoords);
if(iTexCoords >= C4Shader_MaxTexCoords)
return -1;
// Add slices
StdStrBuf Code = FormatString("gl_TexCoord[%d] = gl_MultiTexCoord%d;\n", iTexCoords, iTexCoords);
AddVertexSlice(C4Shader_Vertex_TexCoordPos, Code.getData());
Code.Format("#define %s gl_TexCoord[%d]\n", szName, iTexCoords);
AddFragmentSlice(-1, Code.getData());
return GL_TEXTURE0 + iTexCoords++;
}
void C4Shader::ClearSlices()
{
VertexSlices.clear();
FragmentSlices.clear();
iTexCoords = 0;
}
void C4Shader::Clear()
{
if (!hProg) return;
// Need to be detached, then deleted
glDetachObjectARB(hProg, hFrag);
glDetachObjectARB(hProg, hVert);
glDeleteObjectARB(hFrag);
glDeleteObjectARB(hVert);
glDeleteObjectARB(hProg);
hFrag = hVert = hProg = 0;
// Clear uniform data
delete[] pUniforms; pUniforms = NULL;
iUniformCount = 0;
}
bool C4Shader::Init(const char *szWhat, const char **szUniforms)
{
// No support?
if(!GLEW_ARB_fragment_program)
{
Log(" gl: no shader support!");
return false;
}
// Clear old shader first
if (hProg) Clear();
// Dump
LogSilent(Build(VertexSlices, true).getData());
LogSilent(Build(FragmentSlices, true).getData());
// Attempt to create shaders
StdStrBuf VertexShader = Build(VertexSlices),
FragmentShader = Build(FragmentSlices);
hVert = Create(GL_VERTEX_SHADER_ARB,
FormatString("%s vertex shader", szWhat).getData(),
VertexShader.getData());
hFrag = Create(GL_FRAGMENT_SHADER_ARB,
FormatString("%s fragment shader", szWhat).getData(),
FragmentShader.getData());
if(!hFrag || !hVert)
return false;
// Link program
hProg = glCreateProgramObjectARB();
glAttachObjectARB(hProg, hVert);
glAttachObjectARB(hProg, hFrag);
glLinkProgramARB(hProg);
// Link successful?
DumpInfoLog(FormatString("%s shader program", szWhat).getData(), hProg);
if(GetObjectStatus(hProg, GL_OBJECT_LINK_STATUS_ARB) != 1) {
Clear();
LogF(" gl: Failed to link %s shader!", szWhat);
return false;
}
LogF(" gl: %s shader linked successfully", szWhat);
// Okay, allocate uniform array
iUniformCount = 0;
while (szUniforms[iUniformCount])
iUniformCount++;
pUniforms = new GLint[iUniformCount];
// 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 < iUniformCount; i++)
pUniforms[i] = glGetUniformLocationARB(hProg, szUniforms[i]);
return true;
}
bool C4Shader::Refresh(const char *szWhat, const char **szUniforms)
{
// Find a slice where the source file has updated
ShaderSliceList::iterator pSlice;
for (pSlice = FragmentSlices.begin(); pSlice != FragmentSlices.end(); pSlice++)
if (pSlice->Source.getLength() &&
FileExists(pSlice->Source.getData()) &&
FileTime(pSlice->Source.getData()) > pSlice->SourceTime)
break;
if (pSlice == FragmentSlices.end()) return true;
StdCopyStrBuf Source = pSlice->Source;
// Okay, remove all slices that came from this file
ShaderSliceList::iterator pNext;
for (; pSlice != FragmentSlices.end(); pSlice = pNext)
{
pNext = pSlice; pNext++;
if (SEqual(pSlice->Source.getData(), Source.getData()))
FragmentSlices.erase(pSlice);
}
// Load new shader
char szParentPath[_MAX_PATH+1]; C4Group Group;
StdStrBuf Shader;
GetParentPath(Source.getData(),szParentPath);
if(!Group.Open(szParentPath) ||
!Group.LoadEntryString(GetFilename(Source.getData()),&Shader) ||
!Group.Close())
{
LogF(" gl: Failed to refresh %s shader from %s!", szWhat, Source.getData());
return Refresh(szWhat, szUniforms);
}
// Load slices
int iSourceTime = FileTime(Source.getData());
StdStrBuf WhatSrc = FormatString("file %s", Config.AtRelativePath(Source.getData()));
AddSlices(WhatSrc.getData(), Shader.getData(), Source.getData(), iSourceTime);
// Reinitialise
if (!Init(szWhat, szUniforms))
return false;
// Retry
return Refresh(szWhat, szUniforms);
}
StdStrBuf C4Shader::Build(const ShaderSliceList &Slices, bool fDebug)
{
// At the start of the shader set the #version and number of
// available uniforms
StdStrBuf Buf;
GLint iMaxFrags = 0, iMaxVerts = 0;
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB, &iMaxFrags);
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &iMaxVerts);
Buf.Format("#version %d\n"
"#define MAX_FRAGMENT_UNIFORM_COMPONENTS %d\n"
"#define MAX_VERTEX_UNIFORM_COMPONENTS %d\n",
C4Shader_Version, iMaxFrags, iMaxVerts);
// Put slices
int iPos = -1, iNextPos = -1;
do
{
iPos = iNextPos; iNextPos = C4Shader_LastPosition+1;
// Add all slices at the current level
if (fDebug && iPos > 0)
Buf.AppendFormat("\t// Position %d:\n", iPos);
for (ShaderSliceList::const_iterator pSlice = Slices.begin(); pSlice != Slices.end(); pSlice++)
{
if (pSlice->Position < iPos) continue;
if (pSlice->Position > iPos)
{
iNextPos = Min(iNextPos, pSlice->Position);
continue;
}
// Same position - add slice!
if (fDebug)
{
if (pSlice->Source.getLength())
Buf.AppendFormat("\t// Slice from %s:\n", pSlice->Source.getData());
else
Buf.Append("\t// Built-in slice:\n");
}
Buf.Append(pSlice->Text);
if (Buf[Buf.getLength()-1] != '\n')
Buf.AppendChar('\n');
}
// Add seperator - only priority (-1) is top-level
if (iPos == -1) {
Buf.Append("void main() {\n");
}
}
while (iNextPos <= C4Shader_LastPosition);
// Terminate
Buf.Append("}\n");
return Buf;
}
GLhandleARB C4Shader::Create(GLenum iShaderType, const char *szWhat, const char *szShader)
{
// Create shader
GLhandleARB hShader = glCreateShaderObjectARB(iShaderType);
// Compile
glShaderSourceARB(hShader, 1, &szShader, 0);
glCompileShaderARB(hShader);
// Dump any information to log
DumpInfoLog(szWhat, hShader);
// Success?
if(GetObjectStatus(hShader, GL_OBJECT_COMPILE_STATUS_ARB) == 1)
return hShader;
// Did not work :/
glDeleteObjectARB(hShader);
return 0;
}
void C4Shader::DumpInfoLog(const char *szWhat, GLhandleARB hShader)
{
// Get length of info line
int iLength = 0;
glGetObjectParameterivARB(hShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &iLength);
if(iLength <= 1) return;
// Allocate buffer, get data
char *pBuf = new char [iLength + 1];
int iActualLength = 0;
glGetInfoLogARB(hShader, iLength, &iActualLength, pBuf);
if(iActualLength > iLength || iActualLength <= 0) return;
// Terminate, log
pBuf[iActualLength] = '\0';
LogSilentF(" gl: Compiling %s:", szWhat);
LogSilent(pBuf);
delete[] pBuf;
}
int C4Shader::GetObjectStatus(GLhandleARB hObj, GLenum type)
{
int iStatus = 0;
glGetObjectParameterivARB(hObj, type, &iStatus);
return iStatus;
}
GLint C4ShaderCall::AllocTexUnit(int iUniform, GLenum iType)
{
// Want to bind uniform automatically? If not, the caller will take
// care of it.
if (iUniform >= 0) {
// If uniform isn't used, we should skip this. Also check texunit range.
if (!pShader->HaveUniform(iUniform)) return 0;
assert(iUnits < C4ShaderCall_MaxUnits);
if (iUnits >= C4ShaderCall_MaxUnits) return 0;
// Set the uniform
SetUniform1i(iUniform, iUnits);
}
// Activate the texture
GLint hTex = GL_TEXTURE0 + iUnits;
glActiveTexture(hTex);
hUnit[iUnits] = iType;
glEnable(iType);
iUnits++;
return hTex;
}
void C4ShaderCall::Start()
{
assert(!fStarted);
// Activate shader
glUseProgramObjectARB(pShader->hProg);
fStarted = true;
}
void C4ShaderCall::Finish()
{
// Remove shader
if (fStarted) {
glUseProgramObjectARB(0);
}
// Deactivate all texture units
for (int i = 0; i < iUnits; i++)
{
glActiveTexture(GL_TEXTURE0 + i);
glDisable(hUnit[i]);
}
iUnits = 0;
fStarted = false;
// Got an error?
if(int err = glGetError())
{
LogF("GL error: %d", err /*, gluErrorString(err)*/);
}
}

View File

@ -0,0 +1,144 @@
// Shader implementation somewhere in the middle between easy and extensible.
#ifndef INC_C4Shader
#define INC_C4Shader
#include "StdBuf.h"
#include "C4Surface.h"
// Shader version
const int C4Shader_Version = 110; // GLSL 1.10
// Maximum number of texture coordinates
const int C4Shader_MaxTexCoords = 8;
// Maximum number of texture units per shader call
const int C4ShaderCall_MaxUnits = 32;
// Positions in fragment shader
const int C4Shader_PositionInit = 0;
const int C4Shader_PositionCoordinate = 20;
const int C4Shader_PositionTexture = 40;
const int C4Shader_PositionMaterial = 60;
const int C4Shader_PositionNormal = 80;
const int C4Shader_PositionLight = 100;
const int C4Shader_PositionColor = 120;
const int C4Shader_PositionFinish = 140;
const int C4Shader_LastPosition = 256;
// Positions in vertex shader
const int C4Shader_Vertex_TexCoordPos = 50;
const int C4Shader_Vertex_PositionPos = 80;
class C4Shader
{
friend class C4ShaderCall;
public:
C4Shader();
~C4Shader();
private:
// Program texts
struct ShaderSlice {
int Position;
StdCopyStrBuf Text;
StdCopyStrBuf Source;
int SourceTime;
};
typedef std::list<ShaderSlice> ShaderSliceList;
ShaderSliceList VertexSlices, FragmentSlices;
// Used texture coordinates
int iTexCoords;
// shaders
GLhandleARB hVert, hFrag, hProg;
// shader variables
int iUniformCount;
GLint *pUniforms;
public:
bool Initialised() const { return hVert != 0; }
// Uniform getters
GLint GetUniform(int iUniform) const {
return iUniform >= 0 && iUniform < iUniformCount ? pUniforms[iUniform] : -1;
}
bool HaveUniform(int iUniform) const {
return GetUniform(iUniform) != GLint(-1);
}
// Shader is composed from various slices
void AddVertexSlice(int iPos, const char *szText);
void AddFragmentSlice(int iPos, const char *szText, const char *szSource = "", int iFileTime = 0);
void AddSlices(const char *szWhat, const char *szText, const char *szSource = "", int iFileTime = 0);
bool LoadSlices(C4GroupSet *pGroupSet, const char *szFile);
// Add default vertex code (2D - no transformation)
void AddVertexDefaults();
// Allocate a texture coordinate, returning its ID to be used with glMultiTexCoord.
// The texture coordinate will be visible to both shaders under the given name.
// Note that in contrast to uniforms, these will not disappear if not used!
GLenum AddTexCoord(const char *szName);
// Assemble and link the shader. Should be called again after new slices are added.
bool Init(const char *szWhat, const char **szUniforms);
bool Refresh(const char *szWhat, const char **szUniforms);
void ClearSlices();
void Clear();
private:
int ParsePosition(const char *szWhat, const char **ppPos);
StdStrBuf Build(const ShaderSliceList &Slices, bool fDebug = false);
GLhandleARB Create(GLenum iShaderType, const char *szWhat, const char *szShader);
void DumpInfoLog(const char *szWhat, GLhandleARB hShader);
int GetObjectStatus(GLhandleARB hObj, GLenum type);
};
class C4ShaderCall
{
public:
C4ShaderCall(C4Shader *pShader)
: fStarted(false), pShader(pShader), iUnits(0)
{ }
~C4ShaderCall() { Finish(); }
private:
bool fStarted;
C4Shader *pShader;
int iUnits;
GLenum hUnit[C4ShaderCall_MaxUnits];
public:
GLint AllocTexUnit(int iUniform, GLenum iType);
// Setting uniforms... Lots of code duplication here, not quite sure whether
// something could be done about it.
void SetUniform1i(int iUniform, int iX) const {
if (pShader->HaveUniform(iUniform))
glUniform1iARB(pShader->GetUniform(iUniform), iX);
}
void SetUniform2f(int iUniform, float gX, float gY) const {
if (pShader->HaveUniform(iUniform))
glUniform2fARB(pShader->GetUniform(iUniform), gX, gY);
}
void SetUniform1iv(int iUniform, int iLength, int *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform1ivARB(pShader->GetUniform(iUniform), iLength, pVals);
}
void SetUniform1fv(int iUniform, int iLength, float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform1fvARB(pShader->GetUniform(iUniform), iLength, pVals);
}
void Start();
void Finish();
};
#endif // INC_C4Shader

View File

@ -32,46 +32,13 @@
const int C4LR_BiasDistanceX = 8;
const int C4LR_BiasDistanceY = 8;
// Workarounds to try if shader fails to compile
const char *C4LR_ShaderWorkarounds[] = {
"#define NO_TEXTURE_LOD_IN_FRAGMENT\n",
"#define NO_BROKEN_ARRAYS_WORKAROUND\n",
"#define SCALER_IN_GPU\n",
};
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)
{
switch(iUniform)
{
case C4LRU_LandscapeTex: return "landscapeTex";
case C4LRU_ScalerTex: return "scalerTex";
case C4LRU_MaterialTex: return "materialTex";
case C4LRU_LightTex: return "lightTex";
case C4LRU_AmbientTex: return "ambientTex";
case C4LRU_Resolution: return "resolution";
case C4LRU_Center: return "center";
case C4LRU_MatMap: return "matMap";
case C4LRU_MatMapTex: return "matMapTex";
case C4LRU_MaterialDepth:return "materialDepth";
case C4LRU_MaterialSize: return "materialSize";
case C4LRU_AmbientScale: return "ambientScale";
}
assert(false);
return "mysterious";
}
C4LandscapeRenderGL::C4LandscapeRenderGL()
: iLandscapeShaderTime(0),
hVert(0), hFrag(0), hProg(0)
: iLandscapeShaderTime(0)
{
ZeroMem(Surfaces, sizeof(Surfaces));
ZeroMem(hMaterialTexture, sizeof(hMaterialTexture));
ZeroMem(hUniforms, sizeof(hUniforms));
}
C4LandscapeRenderGL::~C4LandscapeRenderGL()
@ -158,7 +125,7 @@ void C4LandscapeRenderGL::Clear()
hMaterialTexture[i] = 0;
}
LandscapeShader.Clear();
Shader.Clear(); Shader.ClearSlices();
LandscapeShaderPath.Clear();
iLandscapeShaderTime = 0;
}
@ -201,7 +168,7 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
// Find the largest texture
C4Texture *pTex, *pRefTex; C4Surface *pRefSfc = NULL;
for(int iTexIx = 0; pTex = pTexs->GetTexture(pTexs->GetTexture(iTexIx)); iTexIx++)
for(int iTexIx = 0; (pTex = pTexs->GetTexture(pTexs->GetTexture(iTexIx))); iTexIx++)
if(C4Surface *pSfc = pTex->Surface32)
if (!pRefSfc || pRefSfc->Wdt < pSfc->Wdt || pRefSfc->Hgt < pSfc->Hgt)
{ pRefTex = pTex; pRefSfc = pSfc; }
@ -431,7 +398,6 @@ void C4LandscapeRenderGL::Update(C4Rect To, C4Landscape *pSource)
// Biases
int iPix = pSource->_GetPix(To.x+x, To.y+y);
int iPlac = pSource->_GetPlacement(To.x+x, To.y+y);
int iMat = pSource->_GetMat(To.x+x, To.y+y);
int iHBias = Max(0, iPlac * (C4LR_BiasDistanceY-1) - iRight) -
Max(0, iPlac * (C4LR_BiasDistanceY-1) - iLeft);
int iVBias = Max(0, iPlac * (C4LR_BiasDistanceY-1) - pDown[x]) -
@ -532,145 +498,50 @@ void C4LandscapeRenderGL::Update(C4Rect To, C4Landscape *pSource)
}
void C4LandscapeRenderGL::DumpInfoLog(const char *szWhat, GLhandleARB hShader, int32_t iWorkaround)
const char *C4LandscapeRenderGL::UniformNames[C4LRU_Count+1];
bool C4LandscapeRenderGL::LoadShaders(C4GroupSet *pGroups)
{
// Get length of info line
int iLength = 0;
glGetObjectParameterivARB(hShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &iLength);
if(iLength <= 1) return;
// Allocate buffer, get data
char *pBuf = new char [iLength + 1];
int iActualLength = 0;
glGetInfoLogARB(hShader, iLength, &iActualLength, pBuf);
if(iActualLength > iLength || iActualLength <= 0) return;
// Terminate, log
pBuf[iActualLength] = '\0';
LogSilentF(" gl: Compiling %s %d:", szWhat, iWorkaround);
LogSilent(pBuf);
delete[] pBuf;
}
int C4LandscapeRenderGL::GetObjectStatus(GLhandleARB hObj, GLenum type)
{
int iStatus = 0;
glGetObjectParameterivARB(hObj, type, &iStatus);
return iStatus;
}
GLhandleARB C4LandscapeRenderGL::CreateShader(GLenum iShaderType, const char *szWhat, const char *szCode, int32_t iWorkaround)
{
// Create shader
GLhandleARB hShader = glCreateShaderObjectARB(iShaderType);
// Find #version
StdStrBuf Version("");
const char *szCodeRest = szCode;
if (const char *szVersion = SSearch(szCode, "#version"))
{
while (*szVersion && *szVersion != '\n')
szVersion++;
if (*szVersion == '\n')
szVersion++;
Version.Copy(szCode, szVersion - szCode);
szCodeRest = szVersion;
}
// Get number of available uniforms from driver
GLint max_uniforms = 0;
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB, &max_uniforms);
Version.AppendFormat("#define MAX_FRAGMENT_UNIFORM_COMPONENTS %d\n", max_uniforms);
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &max_uniforms);
Version.AppendFormat("#define MAX_VERTEX_UNIFORM_COMPONENTS %d\n", max_uniforms);
// Build code
const char *szCodes[C4LR_ShaderWorkaroundCount + 2];
szCodes[0] = Version.getData();
for(int i = 0; i < C4LR_ShaderWorkaroundCount; i++)
if(iWorkaround & (1 << i))
szCodes[i+1] = C4LR_ShaderWorkarounds[i];
else
szCodes[i+1] = "";
szCodes[C4LR_ShaderWorkaroundCount+1] = szCodeRest;
// Compile
glShaderSourceARB(hShader, C4LR_ShaderWorkaroundCount + 2, szCodes, 0);
glCompileShaderARB(hShader);
// Dump any information to log
DumpInfoLog(szWhat, hShader, iWorkaround);
// Success?
if(GetObjectStatus(hShader, GL_OBJECT_COMPILE_STATUS_ARB) == 1)
return hShader;
// Did not work :/
glDeleteObjectARB(hShader);
return 0;
}
bool C4LandscapeRenderGL::InitShaders()
{
// Already initialized or no shader load?
if(hProg || LandscapeShader.getLength() <= 0)
return false;
// No support?
if(!GLEW_ARB_fragment_program)
{
Log(" gl: no shader support!");
Log(" gl: no shader support!");
return false;
}
// Try all workarounds until one works
int iWorkaround;
for(iWorkaround = 0; iWorkaround < (1 << C4LR_ShaderWorkaroundCount); iWorkaround++)
{
// Create trivial fragment shader
const char *szVert = "#version 110\nvoid main() { gl_TexCoord[0] = gl_MultiTexCoord0; gl_TexCoord[1] = gl_MultiTexCoord1; gl_Position = ftransform(); } ";
hVert = CreateShader(GL_VERTEX_SHADER_ARB, "vertex shader", szVert, iWorkaround);
hFrag = CreateShader(GL_FRAGMENT_SHADER_ARB, "fragment shader", LandscapeShader.getData(), iWorkaround);
if(!hFrag || !hVert)
continue;
// First, clear out all existing shaders
ClearShaders();
// Link program
hProg = glCreateProgramObjectARB();
glAttachObjectARB(hProg, hVert);
glAttachObjectARB(hProg, hFrag);
glLinkProgramARB(hProg);
// Create vertex shader (hard-coded)
Shader.AddVertexDefaults();
hLandscapeTexCoord = Shader.AddTexCoord("landscapeCoord");
hLightTexCoord = Shader.AddTexCoord("lightCoord");
// Link successful?
DumpInfoLog("shader program", hProg, iWorkaround);
if(GetObjectStatus(hProg, GL_OBJECT_LINK_STATUS_ARB) == 1)
break;
// Then load slices for fragment shader
Shader.LoadSlices(pGroups, "UtilShader.c");
Shader.LoadSlices(pGroups, "LandscapeShader.c");
Shader.LoadSlices(pGroups, "LightShader.c");
Shader.LoadSlices(pGroups, "AmbientShader.c");
Shader.LoadSlices(pGroups, "ScalerShader.c");
// Clear up
glDetachObjectARB(hProg, hVert);
glDetachObjectARB(hProg, hFrag);
glDeleteObjectARB(hVert);
glDeleteObjectARB(hFrag);
glDeleteObjectARB(hProg);
hProg = hVert = hFrag = 0;
}
// Did not get it to work?
if(!hProg)
{
Log(" gl: Failed to link shader!");
return false;
}
LogF(" gl: Shader %d linked successfully", iWorkaround);
// Make uniform name map
ZeroMem(UniformNames, sizeof(UniformNames));
UniformNames[C4LRU_LandscapeTex] = "landscapeTex";
UniformNames[C4LRU_ScalerTex] = "scalerTex";
UniformNames[C4LRU_MaterialTex] = "materialTex";
UniformNames[C4LRU_LightTex] = "lightTex";
UniformNames[C4LRU_AmbientTex] = "ambientTex";
UniformNames[C4LRU_Resolution] = "resolution";
UniformNames[C4LRU_Center] = "center";
UniformNames[C4LRU_MatMap] = "matMap";
UniformNames[C4LRU_MatMapTex] = "matMapTex";
UniformNames[C4LRU_MaterialDepth]= "materialDepth";
UniformNames[C4LRU_MaterialSize] = "materialSize";
UniformNames[C4LRU_AmbientScale] = "ambientScale";
// 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())
{
LogF(" gl: Error code %d while linking shader!", err);
// Initialise!
if (!Shader.Init("landscape", UniformNames)) {
Shader.ClearSlices();
return false;
}
return true;
@ -678,37 +549,14 @@ bool C4LandscapeRenderGL::InitShaders()
void C4LandscapeRenderGL::ClearShaders()
{
if (!hProg) return;
// Need to be detached, then deleted
glDetachObjectARB(hProg, hFrag);
glDetachObjectARB(hProg, hVert);
glDeleteObjectARB(hFrag);
glDeleteObjectARB(hVert);
glDeleteObjectARB(hProg);
hFrag = hVert = hProg = 0;
ZeroMem(hUniforms, sizeof(hUniforms));
if (!Shader.Initialised()) return;
Shader.Clear();
Shader.ClearSlices();
}
bool C4LandscapeRenderGL::LoadShaders(C4GroupSet *pGroups)
void C4LandscapeRenderGL::RefreshShaders()
{
// First, clear out all existing shaders
ClearShaders();
// Search for our shaders
C4Group *pGroup = pGroups->FindEntry(C4CFN_LandscapeShader);
if(!pGroup) return false;
// Load it, save the path for later reloading
if(!pGroup->LoadEntryString(C4CFN_LandscapeShader, &LandscapeShader))
return false;
// If it physically exists, save back file name
if(FileExists(pGroup->GetFullName().getData()))
{
LandscapeShaderPath.Format("%s" DirSep C4CFN_LandscapeShader, pGroup->GetFullName().getData());
iLandscapeShaderTime = FileTime(LandscapeShaderPath.getData());
}
// Initialize
return InitShaders();
Shader.Refresh("landscape", UniformNames);
}
bool C4LandscapeRenderGL::LoadScaler(C4GroupSet *pGroups)
@ -806,26 +654,6 @@ bool C4LandscapeRenderGL::LoadScaler(C4GroupSet *pGroups)
return fctScaler.Surface->Unlock();
}
void C4LandscapeRenderGL::RefreshShaders()
{
// File changed?
if(!LandscapeShaderPath.isNull() &&
FileTime(LandscapeShaderPath.getData()) != iLandscapeShaderTime)
{
ClearShaders();
// Load new shader
char szParentPath[_MAX_PATH+1]; C4Group Group;
GetParentPath(LandscapeShaderPath.getData(),szParentPath);
if(!Group.Open(szParentPath) ||
!Group.LoadEntryString(GetFilename(LandscapeShaderPath.getData()),&LandscapeShader) ||
!Group.Close())
return;
// Reinitialize
InitShaders();
iLandscapeShaderTime = FileTime(LandscapeShaderPath.getData());
}
}
int32_t C4LandscapeRenderGL::LookupTextureTransition(const char *szFrom, const char *szTo)
{
// Is this actually a transition? Otherwise we're looking for a single texture
@ -976,7 +804,7 @@ void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap)
void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion &Light)
{
// Must have GL and be initialized
if(!pGL && !hProg) return;
if(!pGL && !Shader.Initialised()) return;
// prepare rendering to surface
C4Surface *sfcTarget = cgo.Surface;
@ -990,58 +818,42 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion &Ligh
while(glGetError()) {}
// Activate shader
glUseProgramObjectARB(hProg);
C4ShaderCall ShaderCall(&Shader);
ShaderCall.Start();
// Bind data
if (hUniforms[C4LRU_Resolution] != GLhandleARB(-1))
glUniform2fARB(hUniforms[C4LRU_Resolution], Surfaces[0]->Wdt, Surfaces[0]->Hgt);
if (hUniforms[C4LRU_Center] != GLhandleARB(-1))
{
float x = float(cgo.TargetX)+float(cgo.Wdt)/2,
y = float(cgo.TargetY)+float(cgo.Hgt)/2;
glUniform2fARB(hUniforms[C4LRU_Center],
x / float(Surfaces[0]->Wdt), y / float(Surfaces[0]->Hgt));
}
if (hUniforms[C4LRU_MatMap] != GLhandleARB(-1))
ShaderCall.SetUniform2f(C4LRU_Resolution, Surfaces[0]->Wdt, Surfaces[0]->Hgt);
float centerX = float(cgo.TargetX)+float(cgo.Wdt)/2,
centerY = float(cgo.TargetY)+float(cgo.Hgt)/2;
ShaderCall.SetUniform2f(C4LRU_Center,
centerX / float(Surfaces[0]->Wdt),
centerY / float(Surfaces[0]->Hgt));
if (Shader.HaveUniform(C4LRU_MatMap))
{
GLfloat MatMap[256];
BuildMatMap(MatMap, NULL);
glUniform1fvARB(hUniforms[C4LRU_MatMap], 256, MatMap);
}
if (hUniforms[C4LRU_MaterialDepth] != GLhandleARB(-1))
glUniform1iARB(hUniforms[C4LRU_MaterialDepth], iMaterialTextureDepth);
if (hUniforms[C4LRU_MaterialSize] != GLhandleARB(-1))
glUniform2fARB(hUniforms[C4LRU_MaterialSize], float(iMaterialWidth) / ::Game.C4S.Landscape.MaterialZoom,
float(iMaterialHeight) / ::Game.C4S.Landscape.MaterialZoom);
if (hUniforms[C4LRU_AmbientScale] != GLhandleARB(-1))
{
// factor between actual landscape size and surface size, so that we can use the landscape
// coordinates for the ambient map lookup.
// TODO: These could actually be shader constants, and do not really need to be uniforms...
const float sx = static_cast<float>(Surfaces[0]->Wdt) / iWidth;
const float sy = static_cast<float>(Surfaces[0]->Hgt) / iHeight;
glUniform2fARB(hUniforms[C4LRU_AmbientScale], sx, sy);
ShaderCall.SetUniform1fv(C4LRU_MatMap, 256, MatMap);
}
ShaderCall.SetUniform1i(C4LRU_MaterialDepth, iMaterialTextureDepth);
ShaderCall.SetUniform2f(C4LRU_MaterialSize,
float(iMaterialWidth) / ::Game.C4S.Landscape.MaterialZoom,
float(iMaterialHeight) / ::Game.C4S.Landscape.MaterialZoom);
// 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 != GLhandleARB(-1)) glUniform1iARB(hUniform, iUnit); \
glActiveTexture(GL_TEXTURE0 + iUnit); \
iUnitMap[iUnit] = iType; \
glEnable(iType); \
iUnit++; \
assert(iUnit < 32); \
} while(false)
// factor between actual landscape size and surface size, so that we can use the landscape
// coordinates for the ambient map lookup.
// TODO: These could actually be shader constants, and do not really need to be uniforms...
ShaderCall.SetUniform2f(C4LRU_AmbientScale,
static_cast<float>(Surfaces[0]->Wdt) / iWidth,
static_cast<float>(Surfaces[0]->Hgt) / iHeight);
// Start binding textures
if(hUniforms[C4LRU_LandscapeTex] != GLhandleARB(-1))
if(Shader.HaveUniform(C4LRU_LandscapeTex))
{
GLint iLandscapeUnits[C4LR_SurfaceCount];
for(int i = 0; i < C4LR_SurfaceCount; i++)
{
iLandscapeUnits[i] = iUnit;
ALLOC_UNIT(GLhandleARB(-1), GL_TEXTURE_2D);
iLandscapeUnits[i] = ShaderCall.AllocTexUnit(-1, GL_TEXTURE_2D) - GL_TEXTURE0;
glBindTexture(GL_TEXTURE_2D, Surfaces[i]->ppTex[0]->texName);
if (pGL->Zoom != 1.0)
{
@ -1054,34 +866,29 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion &Ligh
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
}
glUniform1ivARB(hUniforms[C4LRU_LandscapeTex], C4LR_SurfaceCount, iLandscapeUnits);
ShaderCall.SetUniform1iv(C4LRU_LandscapeTex, C4LR_SurfaceCount, iLandscapeUnits);
}
if(hUniforms[C4LRU_LightTex] != GLhandleARB(-1))
if(ShaderCall.AllocTexUnit(C4LRU_LightTex, GL_TEXTURE_2D))
{
ALLOC_UNIT(hUniforms[C4LRU_LightTex], GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Light.getSurface()->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_AmbientTex] != GLhandleARB(-1))
if(ShaderCall.AllocTexUnit(C4LRU_AmbientTex, GL_TEXTURE_2D))
{
ALLOC_UNIT(hUniforms[C4LRU_AmbientTex], GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Light.getFoW()->Ambient.Tex);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
if(hUniforms[C4LRU_ScalerTex] != GLhandleARB(-1))
if(ShaderCall.AllocTexUnit(C4LRU_ScalerTex, GL_TEXTURE_2D))
{
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] != GLhandleARB(-1))
if(ShaderCall.AllocTexUnit(C4LRU_MaterialTex, GL_TEXTURE_3D))
{
ALLOC_UNIT(hUniforms[C4LRU_MaterialTex], GL_TEXTURE_3D);
// Decide which mip-map level to use
// Decide which mip-map level to use
double z = 0.5; int iMM = 0;
while(pGL->Zoom < z * ::Game.C4S.Landscape.MaterialZoom && iMM + 1 <C4LR_MipMapCount)
{ z /= 2; iMM++; }
@ -1089,9 +896,8 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion &Ligh
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
if(hUniforms[C4LRU_MatMapTex] != GLhandleARB(-1))
if(ShaderCall.AllocTexUnit(C4LRU_MatMapTex, GL_TEXTURE_1D))
{
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);
@ -1124,19 +930,17 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion &Ligh
tTexBlt.top = ty;
tTexBlt.right = tx + float(cgo.Wdt) * pGL->Zoom;
tTexBlt.bottom= ty + float(cgo.Hgt) * pGL->Zoom;
// Blend it
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#if 1
// To the blit
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_QUADS);
#define VERTEX(x, y) \
glMultiTexCoord2f(GL_TEXTURE0, fTexBlt.x, fTexBlt.y); \
glMultiTexCoord2f(GL_TEXTURE1, lTexBlt.x, lTexBlt.y); \
glMultiTexCoord2f(hLandscapeTexCoord, fTexBlt.x, fTexBlt.y); \
glMultiTexCoord2f(hLightTexCoord, lTexBlt.x, lTexBlt.y); \
glVertex2f(tTexBlt.x, tTexBlt.y);
VERTEX(left, top);
@ -1145,59 +949,12 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion &Ligh
VERTEX(left, bottom);
#undef VERTEX
glEnd();
#else
// blit positions
C4BltVertex Vtx[4];
Vtx[0].ftx = tTexBlt.left; Vtx[0].fty = tTexBlt.top;
Vtx[1].ftx = tTexBlt.right; Vtx[1].fty = tTexBlt.top;
Vtx[2].ftx = tTexBlt.right; Vtx[2].fty = tTexBlt.bottom;
Vtx[3].ftx = tTexBlt.left; Vtx[3].fty = tTexBlt.bottom;
Vtx[0].tx = fTexBlt.left; Vtx[0].ty = fTexBlt.top;
Vtx[1].tx = fTexBlt.right; Vtx[1].ty = fTexBlt.top;
Vtx[2].tx = fTexBlt.right; Vtx[2].ty = fTexBlt.bottom;
Vtx[3].tx = fTexBlt.left; Vtx[3].ty = fTexBlt.bottom;
for (int i=0; i<4; ++i)
{
Vtx[i].tx /= float(Surfaces[0]->Wdt);
Vtx[i].ty /= float(Surfaces[0]->Hgt);
Vtx[i].ftz = 0;
Vtx[i].color[0] = 255;
Vtx[i].color[1] = 255;
Vtx[i].color[2] = 255;
Vtx[i].color[3] = 255;
//DwTo4UB(RGBA(255, 255, 255, 255), Vtx[i].color);
}
// color modulation?
//DWORD dwModClr = BlitModulated ? BlitModulateClr : 0xffffffff;
//for (int i=0; i<4; ++i)
// DwTo4UB(dwModClr | dwModMask, Vtx[i].color);
// Blit
glInterleavedArrays(GL_T2F_C4UB_V3F, sizeof(C4BltVertex), Vtx);
glDrawArrays(GL_QUADS, 0, 4);
#endif
// Remove shader
glUseProgramObjectARB(0);
ShaderCall.Finish();
// Unbind textures
while(iUnit > 0)
{
iUnit--;
glActiveTexture(GL_TEXTURE0 + iUnit);
glDisable(iUnitMap[iUnit]);
}
// Got an error?
if(int err = glGetError())
{
LogF("GL error: %d", err /*, gluErrorString(err)*/);
}
}
#endif // #ifndef USE_CONSOLE

View File

@ -4,6 +4,7 @@
#include "C4Surface.h"
#include "C4FacetEx.h"
#include "C4Shader.h"
// Data we want to store per landscape pixel
enum C4LR_Byte {
@ -89,13 +90,12 @@ private:
C4Surface *Surfaces[C4LR_SurfaceCount];
// shader sources
StdStrBuf LandscapeShader;
StdStrBuf LandscapeShaderPath;
int iLandscapeShaderTime;
// shaders
GLhandleARB hVert, hFrag, hProg;
// shader variables
GLhandleARB hUniforms[C4LRU_Count];
// shader
C4Shader Shader;
static const char *UniformNames[];
GLenum hLandscapeTexCoord, hLightTexCoord;
// 3D texture of material textures
GLuint hMaterialTexture[C4LR_MipMapCount];
@ -109,7 +109,6 @@ private:
// scaler image
C4FacetSurface fctScaler;
public:
virtual bool ReInit(int32_t iWidth, int32_t iHeight);
virtual bool Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pMap, C4GroupSet *pGraphics);
@ -126,15 +125,9 @@ private:
bool InitLandscapeTexture();
bool InitMaterialTexture(C4TextureMap *pMap);
bool LoadShaders(C4GroupSet *pGraphics);
void ClearShaders();
bool LoadScaler(C4GroupSet *pGraphics);
void DumpInfoLog(const char *szWhat, GLhandleARB hShader, int32_t iWorkaround);
int GetObjectStatus(GLhandleARB hObj, GLenum type);
GLhandleARB CreateShader(GLenum iShaderType, const char *szWhat, const char *szCode, int32_t iWorkaround);
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);