Avoid built-in GL matrices for sprite rendering

Instead, compute the projection, modelview and normal matrices explictly
and upload them as shader uniforms. This is one step towards using the
OpenGL 3 core profile.
shapetextures
Armin Burgmeier 2015-12-19 22:37:36 -08:00
parent 609840fda0
commit a9967e7b16
9 changed files with 190 additions and 53 deletions

View File

@ -2,6 +2,7 @@
uniform mat3x2 lightTransform;
#ifdef OC_WITH_NORMALMAP
uniform mat3 normalMatrix;
uniform sampler2D normalTex;
#endif
@ -69,7 +70,13 @@ slice(normal)
#ifdef OC_WITH_NORMALMAP
vec4 normalPx = texture2D(normalTex, texcoord.xy);
vec3 normalPxDir = 2.0 * (normalPx.xyz - vec3(0.5, 0.5, 0.5));
#ifdef OC_MESH
// TODO: Change to normalMatrix once we changed mesh rendering
// to use custom matrices
vec3 normal = normalize(gl_NormalMatrix * normalPxDir);
#else
vec3 normal = normalize(normalMatrix * normalPxDir);
#endif
#else
#ifdef OC_MESH
vec3 normal = normalDir; // Normal matrix is already applied in vertex shader

View File

@ -173,9 +173,39 @@ bool CStdGL::UpdateClipper()
glLoadIdentity();
gluOrtho2D((GLdouble) clipRect.x, (GLdouble) (clipRect.x + clipRect.Wdt), (GLdouble) (clipRect.y + clipRect.Hgt), (GLdouble) clipRect.y);
UpdateProjectionMatrix();
return true;
}
void CStdGL::UpdateProjectionMatrix()
{
const C4Rect clipRect = GetClipRect();
const float left = clipRect.x;
const float right = clipRect.x + clipRect.Wdt;
const float bottom = clipRect.y + clipRect.Hgt;
const float top = clipRect.y;
const float near = -1.0f;
const float far = +1.0f;
ProjectionMatrix[0] = 2.0f / (right - left);
ProjectionMatrix[1] = 0.0f;
ProjectionMatrix[2] = 0.0f;
ProjectionMatrix[3] = -(right + left) / (right - left);
ProjectionMatrix[4] = 0.0f;
ProjectionMatrix[5] = 2.0f / (top - bottom);
ProjectionMatrix[6] = 0.0f;
ProjectionMatrix[7] = -(top + bottom) / (top - bottom);
ProjectionMatrix[8] = 0.0f;
ProjectionMatrix[9] = 0.0f;
ProjectionMatrix[10] = -2.0f / (far - near);
ProjectionMatrix[11] = -(far + near) / (far - near);
ProjectionMatrix[12] = 0.0f;
ProjectionMatrix[13] = 0.0f;
ProjectionMatrix[14] = 0.0f;
ProjectionMatrix[15] = 1.0f;
}
bool CStdGL::PrepareRendering(C4Surface * sfcToSurface)
{
// call from gfx thread only!
@ -207,11 +237,18 @@ bool CStdGL::PrepareRendering(C4Surface * sfcToSurface)
bool CStdGL::PrepareSpriteShader(C4Shader& shader, const char* name, int ssc, C4GroupSet* pGroups, const char* const* additionalDefines, const char* const* additionalSlices)
{
static const char vertexUniforms[] =
"uniform mat4 projectionMatrix;"
"uniform mat4 modelviewMatrix;";
static const char vertexSlice[] =
" gl_FrontColor = gl_Color;"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;";
" gl_Position = projectionMatrix * modelviewMatrix * gl_Vertex;";
const char* uniformNames[C4SSU_Count + 1];
uniformNames[C4SSU_ProjectionMatrix] = "projectionMatrix";
uniformNames[C4SSU_ModelViewMatrix] = "modelviewMatrix";
uniformNames[C4SSU_NormalMatrix] = "normalMatrix";
uniformNames[C4SSU_ClrMod] = "clrMod";
uniformNames[C4SSU_Gamma] = "gamma";
uniformNames[C4SSU_BaseTex] = "baseTex";
@ -223,14 +260,15 @@ bool CStdGL::PrepareSpriteShader(C4Shader& shader, const char* name, int ssc, C4
uniformNames[C4SSU_AmbientTex] = "ambientTex";
uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
uniformNames[C4SSU_Bones] = "bones";
uniformNames[C4SSU_CullMode] = "cullMode";
uniformNames[C4SSU_Bones] = "bones"; // unused
uniformNames[C4SSU_CullMode] = "cullMode"; // unused
uniformNames[C4SSU_Count] = NULL;
// Clear previous content
shader.Clear();
shader.ClearSlices();
shader.AddVertexSlice(-1, vertexUniforms);
shader.AddVertexSlice(C4Shader_Vertex_PositionPos, vertexSlice);
// Add texture coordinate if we have base texture, overlay, or normal map
@ -384,7 +422,7 @@ bool CStdGL::CreatePrimarySurfaces(unsigned int, unsigned int, int iColorDepth,
return ok;
}
void CStdGL::SetupMultiBlt(C4ShaderCall& call, const C4BltTransform* pTransform, GLuint baseTex, GLuint overlayTex, GLuint normalTex, DWORD dwOverlayModClr)
void CStdGL::SetupMultiBlt(C4ShaderCall& call, const C4BltTransform* pTransform, GLuint baseTex, GLuint overlayTex, GLuint normalTex, DWORD dwOverlayModClr, StdMeshMatrix* out_modelview)
{
// Initialize multi blit shader.
int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;
@ -457,36 +495,57 @@ void CStdGL::SetupMultiBlt(C4ShaderCall& call, const C4BltTransform* pTransform,
call.SetUniform1f(C4SSU_CullMode, 0.0f);
StdMeshMatrix default_modelview = StdMeshMatrix::Identity();
StdMeshMatrix& modelview = out_modelview ? *out_modelview : default_modelview;
// Apply zoom and transform
glPushMatrix();
glTranslatef(ZoomX, ZoomY, 0.0f);
Translate(modelview, ZoomX, ZoomY, 0.0f);
// Scale Z as well so that we don't distort normals.
glScalef(Zoom, Zoom, Zoom);
glTranslatef(-ZoomX, -ZoomY, 0.0f);
Scale(modelview, Zoom, Zoom, Zoom);
Translate(modelview, -ZoomX, -ZoomY, 0.0f);
if(pTransform)
{
// Decompose scale factors and scale Z accordingly to X and Y, again to avoid distorting normals
// We could instead work around this by using the projection matrix, but then for object rotations (SetR)
// the normals would not be correct.
const float sx = sqrt(pTransform->mat[0]*pTransform->mat[0] + pTransform->mat[1]*pTransform->mat[1]);
const float sy = sqrt(pTransform->mat[3]*pTransform->mat[3] + pTransform->mat[4]*pTransform->mat[4]);
const float sz = sqrt(sx * sy);
const GLfloat transform[16] = { pTransform->mat[0], pTransform->mat[3], 0, pTransform->mat[6], pTransform->mat[1], pTransform->mat[4], 0, pTransform->mat[7], 0, 0, sz, 0, pTransform->mat[2], pTransform->mat[5], 0, pTransform->mat[8] };
glMultMatrixf(transform);
}
}
float sz = 1.0f;
if (pFoW != NULL && normalTex != 0)
{
// Decompose scale factors and scale Z accordingly to X and Y, again to avoid distorting normals
// We could instead work around this by using the projection matrix, but then for object rotations (SetR)
// the normals would not be correct.
const float sx = sqrt(pTransform->mat[0]*pTransform->mat[0] + pTransform->mat[1]*pTransform->mat[1]);
const float sy = sqrt(pTransform->mat[3]*pTransform->mat[3] + pTransform->mat[4]*pTransform->mat[4]);
sz = sqrt(sx * sy);
}
void CStdGL::ResetMultiBlt()
{
glPopMatrix();
// Multiply modelview matrix with transform
StdMeshMatrix transform;
transform(0, 0) = pTransform->mat[0];
transform(0, 1) = pTransform->mat[1];
transform(0, 2) = 0.0f;
transform(0, 3) = pTransform->mat[2];
transform(1, 0) = pTransform->mat[3];
transform(1, 1) = pTransform->mat[4];
transform(1, 2) = 0.0f;
transform(1, 3) = pTransform->mat[5];
transform(2, 0) = pTransform->mat[6];
transform(2, 1) = pTransform->mat[7];
transform(2, 2) = sz;
transform(2, 3) = pTransform->mat[8];
modelview *= transform;
}
call.SetUniformMatrix4x4fv(C4SSU_ProjectionMatrix, 1, ProjectionMatrix);
call.SetUniformMatrix4x4(C4SSU_ModelViewMatrix, modelview);
if (pFoW != NULL && normalTex != 0)
call.SetUniformMatrix3x3Transpose(C4SSU_NormalMatrix, StdMeshMatrix::Inverse(modelview));
}
void CStdGL::PerformMultiPix(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices, C4ShaderCall* shader_call)
{
// Draw on pixel center:
glPushMatrix();
glTranslatef(0.5f, 0.5f, 0.0f);
StdMeshMatrix transform = StdMeshMatrix::Translate(0.5f, 0.5f, 0.0f);
// This is a workaround. Instead of submitting the whole vertex array to the GL, we do it
// in batches of 256 vertices. The intel graphics driver on Linux crashes with
@ -498,20 +557,16 @@ void CStdGL::PerformMultiPix(C4Surface* sfcTarget, const C4BltVertex* vertices,
if (!shader_call)
{
C4ShaderCall call(GetSpriteShader(false, false, false));
SetupMultiBlt(call, NULL, 0, 0, 0, 0);
SetupMultiBlt(call, NULL, 0, 0, 0, 0, &transform);
for(unsigned int i = 0; i < n_vertices; i += BATCH_SIZE)
PerformMultiBlt(sfcTarget, OP_POINTS, &vertices[i], std::min(n_vertices - i, BATCH_SIZE), false);
ResetMultiBlt();
}
else
{
SetupMultiBlt(*shader_call, NULL, 0, 0, 0, 0);
SetupMultiBlt(*shader_call, NULL, 0, 0, 0, 0, &transform);
for(unsigned int i = 0; i < n_vertices; i += BATCH_SIZE)
PerformMultiBlt(sfcTarget, OP_POINTS, &vertices[i], std::min(n_vertices - i, BATCH_SIZE), false);
ResetMultiBlt();
}
glPopMatrix();
}
void CStdGL::PerformMultiLines(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices, float width, C4ShaderCall* shader_call)
@ -562,15 +617,13 @@ void CStdGL::PerformMultiLines(C4Surface* sfcTarget, const C4BltVertex* vertices
if (!shader_call)
{
C4ShaderCall call(GetSpriteShader(true, false, false));
SetupMultiBlt(call, NULL, lines_tex, 0, 0, 0);
SetupMultiBlt(call, NULL, lines_tex, 0, 0, 0, NULL);
PerformMultiBlt(sfcTarget, OP_TRIANGLES, tri_vertices, n_vertices * 3, true);
ResetMultiBlt();
}
else
{
SetupMultiBlt(*shader_call, NULL, lines_tex, 0, 0, 0);
SetupMultiBlt(*shader_call, NULL, lines_tex, 0, 0, 0, NULL);
PerformMultiBlt(sfcTarget, OP_TRIANGLES, tri_vertices, n_vertices * 3, true);
ResetMultiBlt();
}
delete[] tri_vertices;
@ -582,15 +635,13 @@ void CStdGL::PerformMultiTris(C4Surface* sfcTarget, const C4BltVertex* vertices,
if (!shader_call)
{
C4ShaderCall call(GetSpriteShader(pTex != NULL, pOverlay != NULL, pNormal != NULL));
SetupMultiBlt(call, pTransform, pTex ? pTex->texName : 0, pOverlay ? pOverlay->texName : 0, pNormal ? pNormal->texName : 0, dwOverlayModClr);
SetupMultiBlt(call, pTransform, pTex ? pTex->texName : 0, pOverlay ? pOverlay->texName : 0, pNormal ? pNormal->texName : 0, dwOverlayModClr, NULL);
PerformMultiBlt(sfcTarget, OP_TRIANGLES, vertices, n_vertices, pTex != NULL);
ResetMultiBlt();
}
else
{
SetupMultiBlt(*shader_call, pTransform, pTex ? pTex->texName : 0, pOverlay ? pOverlay->texName : 0, pNormal ? pNormal->texName : 0, dwOverlayModClr);
SetupMultiBlt(*shader_call, pTransform, pTex ? pTex->texName : 0, pOverlay ? pOverlay->texName : 0, pNormal ? pNormal->texName : 0, dwOverlayModClr, NULL);
PerformMultiBlt(sfcTarget, OP_TRIANGLES, vertices, n_vertices, pTex != NULL);
ResetMultiBlt();
}
}

View File

@ -49,6 +49,10 @@ private:
// Uniform data we give the sprite shader (constants from its viewpoint)
enum C4SS_Uniforms
{
C4SSU_ProjectionMatrix, // 4x4
C4SSU_ModelViewMatrix, // 4x4
C4SSU_NormalMatrix, // 3x3, transpose-inverse of modelview matrix
C4SSU_ClrMod, // always
C4SSU_Gamma, // always
@ -129,6 +133,10 @@ protected:
int iClrDpt; // color depth
// texture for smooth lines
GLuint lines_tex;
// The orthographic projection matrix
float ProjectionMatrix[16];
// programs for drawing points, lines, quads
// Sprite shaders -- there is a variety of shaders to avoid
@ -159,6 +167,7 @@ public:
virtual bool OnResolutionChanged(unsigned int iXRes, unsigned int iYRes); // reinit clipper for new resolution
// Clipper
bool UpdateClipper(); // set current clipper to render target
void UpdateProjectionMatrix();
virtual bool PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterialLoader& loader, StdMeshMaterial& mat);
// Surface
virtual bool PrepareRendering(C4Surface * sfcToSurface); // check if/make rendering possible to given surface
@ -169,8 +178,7 @@ public:
virtual CStdGLCtx *CreateContext(HWND hWindow, C4AbstractApp *pApp);
#endif
// Blit
void SetupMultiBlt(C4ShaderCall& call, const C4BltTransform* pTransform, GLuint baseTex, GLuint overlayTex, GLuint normalTex, DWORD dwOverlayModClr);
void ResetMultiBlt();
void SetupMultiBlt(C4ShaderCall& call, const C4BltTransform* pTransform, GLuint baseTex, GLuint overlayTex, GLuint normalTex, DWORD dwOverlayModClr, StdMeshMatrix* out_modelview);
virtual void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, DWORD dwPlayerColor, C4BltTransform* pTransform);
void FillBG(DWORD dwClr=0);
// Drawing

View File

@ -19,6 +19,7 @@
#define INC_C4Shader
#include "StdBuf.h"
#include "StdMeshMath.h"
#include "C4Surface.h"
// Shader version
@ -230,6 +231,11 @@ public:
glUniformMatrix3x2fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
}
void SetUniformMatrix3x3fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniformMatrix3fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
}
void SetUniformMatrix3x4fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniformMatrix4x3fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
@ -240,6 +246,39 @@ public:
glUniformMatrix4fvARB(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
}
void SetUniformMatrix3x3(int iUniform, const StdMeshMatrix& matrix)
{
if (pShader->HaveUniform(iUniform))
{
const float mat[9] = { matrix(0, 0), matrix(1, 0), matrix(2, 0), matrix(0, 1), matrix(1, 1), matrix(2, 1), matrix(0, 2), matrix(1, 2), matrix(2, 2) };
glUniformMatrix3fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
}
}
void SetUniformMatrix3x3Transpose(int iUniform, const StdMeshMatrix& matrix)
{
if (pShader->HaveUniform(iUniform))
{
const float mat[9] = { matrix(0, 0), matrix(0, 1), matrix(0, 2), matrix(1, 0), matrix(1, 1), matrix(1, 2), matrix(2, 0), matrix(2, 1), matrix(2, 2) };
glUniformMatrix3fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
}
}
void SetUniformMatrix3x4(int iUniform, const StdMeshMatrix& matrix)
{
if (pShader->HaveUniform(iUniform))
glUniformMatrix4x3fv(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data());
}
void SetUniformMatrix4x4(int iUniform, const StdMeshMatrix& matrix)
{
if (pShader->HaveUniform(iUniform))
{
const float mat[16] = { matrix(0, 0), matrix(1, 0), matrix(2, 0), 0.0f, matrix(0, 1), matrix(1, 1), matrix(1, 2), 0.0f, matrix(2, 0), matrix(2, 1), matrix(2, 2), 0.0f, matrix(0, 3), matrix(1, 3), matrix(2, 3), 1.0f };
glUniformMatrix4fvARB(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
}
}
void Start();
void Finish();
};

View File

@ -978,7 +978,7 @@ bool C4ParticleChunk::Exec(C4Object *obj, float timeDelta)
#define glDeleteVertexArrays glDeleteVertexArraysAPPLE
#endif
void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, int texUnit)
void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call, int texUnit, const StdMeshMatrix& modelview)
{
if (particleCount == 0) return;
const int stride = sizeof(C4Particle::DrawingData::Vertex);
@ -987,12 +987,16 @@ void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, int texUnit)
assert(textureRef != 0 && "Particle definition had no texture assigned.");
// use a relative offset?
bool resetMatrix(false);
// (note the normal matrix is unaffected by this)
if ((attachment & C4ATTACH_MoveRelative) && (obj != 0))
{
resetMatrix = true;
glPushMatrix();
glTranslatef((float)obj->GetX(), (float)obj->GetY(), 0.0f);
StdMeshMatrix new_modelview(modelview);
Translate(new_modelview, fixtof(obj->GetFixedX()), fixtof(obj->GetFixedY()), 0.0f);
call.SetUniformMatrix4x4(C4SSU_ModelViewMatrix, new_modelview);
}
else
{
call.SetUniformMatrix4x4(C4SSU_ModelViewMatrix, modelview);
}
// enable additive blending for particles with that blit mode
@ -1063,8 +1067,6 @@ void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, int texUnit)
{
glMultiDrawElements(GL_TRIANGLE_STRIP, ::Particles.GetMultiDrawElementsCountArray(), GL_UNSIGNED_INT, const_cast<const GLvoid**>(::Particles.GetMultiDrawElementsIndexArray()), static_cast<GLsizei> (particleCount));
}
if (resetMatrix)
glPopMatrix();
// reset buffer data
if (!Particles.useBufferObjectWorkaround)
@ -1158,9 +1160,11 @@ void C4ParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
// enable shader
C4ShaderCall call(pGL->GetSpriteShader(true, false, false));
// apply zoom and upload shader uniforms
pGL->SetupMultiBlt(call, NULL, 0, 0, 0, 0);
// go to correct output position
glTranslatef(cgo.X-cgo.TargetX, cgo.Y-cgo.TargetY, 0.0f);
StdMeshMatrix modelview = StdMeshMatrix::Identity();
pGL->SetupMultiBlt(call, NULL, 0, 0, 0, 0, &modelview);
// go to correct output position (note the normal matrix is unaffected
// by this)
Translate(modelview, cgo.X-cgo.TargetX, cgo.Y-cgo.TargetY, 0.0f);
// allocate texture unit for particle texture, and remember allocated
// texture unit. Will be used for each particle chunk to bind
// their texture to this unit.
@ -1188,15 +1192,13 @@ void C4ParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
}
else
{
(*iter)->Draw(cgo, obj, texUnit);
(*iter)->Draw(cgo, obj, call, texUnit, modelview);
++iter;
}
}
accessMutex.Leave();
pGL->ResetMultiBlt();
if (Particles.useBufferObjectWorkaround)
{
glDisableClientState(GL_VERTEX_ARRAY);

View File

@ -364,7 +364,7 @@ public:
// removes all particles
void Clear();
bool Exec(C4Object *obj, float timeDelta);
void Draw(C4TargetFacet cgo, C4Object *obj, int texUnit);
void Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call, int texUnit, const StdMeshMatrix& modelview);
bool IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t attachment) const;
bool IsEmpty() const { return !particleCount; }

View File

@ -864,6 +864,9 @@ bool StdMeshMaterialProgram::CompileShader(StdMeshMaterialLoader& loader, C4Shad
std::vector<const char*> uniformNames;
#ifndef USE_CONSOLE
uniformNames.resize(C4SSU_Count + ParameterNames.size() + 1);
uniformNames[C4SSU_ProjectionMatrix] = "projectionMatrix"; // unused YET
uniformNames[C4SSU_ModelViewMatrix] = "modelviewMatrix"; // unused YET
uniformNames[C4SSU_NormalMatrix] = "normalMatrix"; // unused YET
uniformNames[C4SSU_ClrMod] = "clrMod";
uniformNames[C4SSU_Gamma] = "gamma";
uniformNames[C4SSU_BaseTex] = "baseTex"; // unused

View File

@ -669,3 +669,23 @@ StdMeshVertex operator*(const StdMeshMatrix& lhs, const StdMeshVertex& rhs)
vtx.u = rhs.u; vtx.v = rhs.v;
return vtx;
}
void Translate(StdMeshMatrix& mat, float dx, float dy, float dz)
{
mat(0, 3) += mat(0,0)*dx + mat(0,1)*dy + mat(0,2)*dz;
mat(1, 3) += mat(1,0)*dx + mat(1,1)*dy + mat(1,2)*dz;
mat(2, 3) += mat(2,0)*dx + mat(2,1)*dy + mat(2,2)*dz;
}
void Scale(StdMeshMatrix& mat, float sx, float sy, float sz)
{
mat(0, 0) *= sx;
mat(1, 0) *= sx;
mat(2, 0) *= sx;
mat(0, 1) *= sy;
mat(1, 1) *= sy;
mat(2, 1) *= sy;
mat(0, 2) *= sz;
mat(1, 2) *= sz;
mat(2, 2) *= sz;
}

View File

@ -102,6 +102,8 @@ public:
float Determinant() const;
StdMeshTransformation Decompose() const;
const float* data() const { return &a[0][0]; }
private:
// 3x3 orthogonal + translation in last column
float a[3][4];
@ -142,4 +144,9 @@ StdMeshVertex operator*(float lhs, const StdMeshVertex& rhs);
StdMeshVertex operator*(const StdMeshVertex& lhs, float rhs);
StdMeshVertex operator*(const StdMeshMatrix& lhs, const StdMeshVertex& rhs);
// Multiply in-place the given matrix with a translation matrix to the right
void Translate(StdMeshMatrix& mat, float dx, float dy, float dz);
// Multiply in-place the given matrix with a scale matrix to the right
void Scale(StdMeshMatrix& mat, float sx, float sy, float sz);
#endif