finish front/back face culling code based on matrix determinant. Optimised opengl to avoid redundant state changes.

This commit is contained in:
Ian Curtis 2016-03-22 23:39:59 +00:00
parent 44ea902181
commit f031e5d095
5 changed files with 106 additions and 111 deletions

View file

@ -69,8 +69,7 @@ struct Model
//matrices //matrices
float modelMat[16]; float modelMat[16];
float determinant; // we check if the determinant of the matrix is negative, if it is, the matrix will swap the axis order
// misc // misc
int lutIdx = 0; int lutIdx = 0;

View file

@ -83,25 +83,26 @@ void CNew3D::RenderScene(int priority, bool alpha)
for (auto &n : m_nodes) { for (auto &n : m_nodes) {
if (n.viewport.priority != priority) { if (n.viewport.priority != priority || n.models.empty()) {
continue; continue;
} }
glViewport(n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height); glViewport (n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height);
glMatrixMode (GL_PROJECTION);
glMatrixMode(GL_PROJECTION); glLoadMatrixf (n.viewport.projectionMatrix);
glLoadMatrixf(n.viewport.projectionMatrix); glMatrixMode (GL_MODELVIEW);
m_r3dShader.SetViewportUniforms(&n.viewport); m_r3dShader.SetViewportUniforms(&n.viewport);
for (auto &m : n.models) { for (auto &m : n.models) {
bool matrixLoaded = false;
if (m.meshes.empty()) { if (m.meshes.empty()) {
continue; continue;
} }
glMatrixMode(GL_MODELVIEW); m_r3dShader.SetModelStates(&m);
glLoadMatrixf(m.modelMat);
for (auto &mesh : m.meshes) { for (auto &mesh : m.meshes) {
@ -116,6 +117,11 @@ void CNew3D::RenderScene(int priority, bool alpha)
} }
} }
if (!matrixLoaded) {
glLoadMatrixf(m.modelMat);
matrixLoaded = true; // do this here to stop loading matrices we don't need. Ie when rendering non transparent etc
}
if (mesh.texture) { if (mesh.texture) {
mesh.texture->BindTexture(); mesh.texture->BindTexture();
mesh.texture->SetWrapMode(mesh.mirrorU, mesh.mirrorV); mesh.texture->SetWrapMode(mesh.mirrorU, mesh.mirrorV);
@ -244,6 +250,9 @@ bool CNew3D::DrawModel(UINT32 modelAddr)
m->modelMat[i] = m_modelMat.currentMatrix[i]; m->modelMat[i] = m_modelMat.currentMatrix[i];
} }
//calculate determinant
m->determinant = Determinant3x3(m_modelMat);
CacheModel(m, modelAddress); CacheModel(m, modelAddress);
return true; return true;
@ -706,15 +715,13 @@ void CNew3D::CopyVertexData(R3DPoly& r3dPoly, std::vector<Poly>& polyArray)
Poly p; Poly p;
V3::Vec3 normal; V3::Vec3 normal;
float dotProd; float dotProd;
float zFlip;
bool clockWise; bool clockWise;
//==================== //====================
V3::createNormal(r3dPoly.v[0].pos, r3dPoly.v[1].pos, r3dPoly.v[2].pos, normal); V3::createNormal(r3dPoly.v[0].pos, r3dPoly.v[1].pos, r3dPoly.v[2].pos, normal);
dotProd = V3::dotProduct(normal, r3dPoly.faceNormal); dotProd = V3::dotProduct(normal, r3dPoly.faceNormal);
zFlip = -1.0f*m_matrixBasePtr[0x5]; // coordinate system m13 component clockWise = dotProd >= 0.0;
clockWise = (zFlip*dotProd >= 0.0);
if (clockWise) { if (clockWise) {
p.p1 = r3dPoly.v[0]; p.p1 = r3dPoly.v[0];
@ -931,8 +938,6 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
} }
} }
bool cw = ClockWiseWinding();
//sorted the data, now copy to main data structures //sorted the data, now copy to main data structures
// we know how many meshes we have so reserve appropriate space // we know how many meshes we have so reserve appropriate space
@ -954,69 +959,6 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
} }
} }
// Macro to generate column-major (OpenGL) index from y,x subscripts
#define CMINDEX(y,x) (x*4+y)
// 3x3 matrix used (upper-left of m[])
static void MultMat3Vec3(GLfloat out[3], GLfloat m[4 * 4], GLfloat v[3])
{
out[0] = m[CMINDEX(0, 0)] * v[0] + m[CMINDEX(0, 1)] * v[1] + m[CMINDEX(0, 2)] * v[2];
out[1] = m[CMINDEX(1, 0)] * v[0] + m[CMINDEX(1, 1)] * v[1] + m[CMINDEX(1, 2)] * v[2];
out[2] = m[CMINDEX(2, 0)] * v[0] + m[CMINDEX(2, 1)] * v[1] + m[CMINDEX(2, 2)] * v[2];
}
static GLfloat Sign(GLfloat x)
{
if (x > 0.0f)
return 1.0f;
else if (x < 0.0f)
return -1.0f;
return 0.0f;
}
// Inverts and transposes a 3x3 matrix (upper-left of the 4x4), returning a
// 4x4 matrix with the extra components undefined (do not use them!)
static void InvertTransposeMat3(GLfloat out[4 * 4], GLfloat m[4 * 4])
{
GLfloat invDet;
GLfloat a00 = m[CMINDEX(0, 0)], a01 = m[CMINDEX(0, 1)], a02 = m[CMINDEX(0, 2)];
GLfloat a10 = m[CMINDEX(1, 0)], a11 = m[CMINDEX(1, 1)], a12 = m[CMINDEX(1, 2)];
GLfloat a20 = m[CMINDEX(2, 0)], a21 = m[CMINDEX(2, 1)], a22 = m[CMINDEX(2, 2)];
invDet = 1.0f / (a00*(a22*a11 - a21*a12) - a10*(a22*a01 - a21*a02) + a20*(a12*a01 - a11*a02));
out[CMINDEX(0, 0)] = invDet*(a22*a11 - a21*a12); out[CMINDEX(1, 0)] = invDet*(-(a22*a01 - a21*a02)); out[CMINDEX(2, 0)] = invDet*(a12*a01 - a11*a02);
out[CMINDEX(0, 1)] = invDet*(-(a22*a10 - a20*a12)); out[CMINDEX(1, 1)] = invDet*(a22*a00 - a20*a02); out[CMINDEX(2, 1)] = invDet*(-(a12*a00 - a10*a02));
out[CMINDEX(0, 2)] = invDet*(a21*a10 - a20*a11); out[CMINDEX(1, 2)] = invDet*(-(a21*a00 - a20*a01)); out[CMINDEX(2, 2)] = invDet*(a11*a00 - a10*a01);
}
bool CNew3D::ClockWiseWinding()
{
GLfloat x[3] = { 1.0f, 0.0f, 0.0f };
GLfloat y[3] = { 0.0f, 1.0f, 0.0f };
GLfloat z[3] = { 0.0f, 0.0f, -1.0f*m_matrixBasePtr[0x5] };
GLfloat m[4 * 4];
GLfloat xT[3], yT[3], zT[3], pT[3];
InvertTransposeMat3(m, m_modelMat.currentMatrix);
MultMat3Vec3(xT, m_modelMat.currentMatrix, x);
MultMat3Vec3(yT, m_modelMat.currentMatrix, y);
MultMat3Vec3(zT, m, z);
V3::crossProduct(pT, xT, yT);
float s = Sign(zT[2] * pT[2]);
if (s < 0.0f) {
return false;
}
else if (s > 0.0f) {
return true;
}
else {
int debugbreak = 0;
return false;
}
}
float CNew3D::Determinant3x3(const float m[16]) { float CNew3D::Determinant3x3(const float m[16]) {
/* /*

View file

@ -169,9 +169,7 @@ private:
void CacheModel(Model *m, const UINT32 *data); void CacheModel(Model *m, const UINT32 *data);
void CopyVertexData(R3DPoly& r3dPoly, std::vector<Poly>& polyArray); void CopyVertexData(R3DPoly& r3dPoly, std::vector<Poly>& polyArray);
enum class AlphaType{ none, poly, texture };
void RenderScene(int priority, bool alpha); void RenderScene(int priority, bool alpha);
bool ClockWiseWinding(); // calculate winding with current matrix
float Determinant3x3(const float m[16]); float Determinant3x3(const float m[16]);
/* /*

View file

@ -143,8 +143,12 @@ void R3DShader::Start()
m_textureAlpha = false; // use alpha in texture m_textureAlpha = false; // use alpha in texture
m_alphaTest = false; // discard fragment based on alpha (ogl does this with fixed function) m_alphaTest = false; // discard fragment based on alpha (ogl does this with fixed function)
m_doubleSided = false; m_doubleSided = false;
m_lightEnabled = false;
m_dirty = true; // dirty means all the above are dirty, ie first run m_matDet = MatDet::notset;
m_dirtyMesh = true; // dirty means all the above are dirty, ie first run
m_dirtyModel = true;
} }
bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader) bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
@ -206,52 +210,61 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
return; // sanity check return; // sanity check
} }
if (m_dirty) { if (m_dirtyMesh) {
glUniform1i(m_locTexture, 0); glUniform1i(m_locTexture, 0);
} }
if (m_dirty || m->textured != m_textured) { if (m_dirtyMesh || m->textured != m_textured) {
glUniform1i(m_locTextureEnabled, m->textured); glUniform1i(m_locTextureEnabled, m->textured);
m_textured = m->textured; m_textured = m->textured;
} }
if (m_dirty || m->alphaTest != m_alphaTest) { if (m_dirtyMesh || m->alphaTest != m_alphaTest) {
glUniform1i(m_locAlphaTest, m->alphaTest); glUniform1i(m_locAlphaTest, m->alphaTest);
m_alphaTest = m->alphaTest; m_alphaTest = m->alphaTest;
} }
if (m_dirty || m->textureAlpha != m_textureAlpha) { if (m_dirtyMesh || m->textureAlpha != m_textureAlpha) {
glUniform1i(m_locTextureAlpha, m->textureAlpha); glUniform1i(m_locTextureAlpha, m->textureAlpha);
m_textureAlpha = m->textureAlpha; m_textureAlpha = m->textureAlpha;
} }
if (m_dirty || m->fogIntensity != m_fogIntensity) { if (m_dirtyMesh || m->fogIntensity != m_fogIntensity) {
glUniform1f(m_locFogIntensity, m->fogIntensity); glUniform1f(m_locFogIntensity, m->fogIntensity);
m_fogIntensity = m->fogIntensity; m_fogIntensity = m->fogIntensity;
} }
glUniform1i(m_locLightEnable, m->lighting); if (m_dirtyMesh || m->lighting != m_lightEnabled) {
glUniform1f(m_locShininess, 1); glUniform1i(m_locLightEnable, m->lighting);
m_lightEnabled = m->lighting;
}
// technically not uniforms //glUniform1f(m_locShininess, 1);
if (m_dirty || m->doubleSided != m_doubleSided) {
m_doubleSided = m->doubleSided; if (m_matDet!=MatDet::zero) {
if (m_doubleSided) {
glDisable(GL_CULL_FACE); if (m_dirtyMesh || m->doubleSided != m_doubleSided) {
}
else { m_doubleSided = m->doubleSided;
glEnable(GL_CULL_FACE);
if (m_doubleSided) {
glDisable(GL_CULL_FACE);
}
else {
glEnable(GL_CULL_FACE);
}
} }
} }
m_dirty = false;
m_dirtyMesh = false;
} }
void R3DShader::SetViewportUniforms(const Viewport *vp) void R3DShader::SetViewportUniforms(const Viewport *vp)
{ {
//didn't bother caching these, they don't get frequently called anyway //didn't bother caching these, they don't get frequently called anyway
glUniform1f(m_locFogDensity, vp->fogParams[3]); glUniform1f (m_locFogDensity, vp->fogParams[3]);
glUniform1f(m_locFogStart, vp->fogParams[4]); glUniform1f (m_locFogStart, vp->fogParams[4]);
glUniform3fv(m_locFogColour, 1, vp->fogParams); glUniform3fv(m_locFogColour, 1, vp->fogParams);
glUniform3fv(m_locLighting, 2, vp->lightingParams); glUniform3fv(m_locLighting, 2, vp->lightingParams);
@ -260,4 +273,37 @@ void R3DShader::SetViewportUniforms(const Viewport *vp)
glUniform3fv(m_locSpotColor, 1, vp->spotColor); glUniform3fv(m_locSpotColor, 1, vp->spotColor);
} }
void R3DShader::SetModelStates(const Model* model)
{
//==========
MatDet test;
//==========
if (model->determinant < 0) { test = MatDet::negative; }
else if (model->determinant > 0) { test = MatDet::positive; }
else if (model->determinant == 0) { test = MatDet::zero; }
if (m_dirtyModel || m_matDet!=test) {
switch (test) {
case MatDet::negative:
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
m_doubleSided = false;
break;
case MatDet::positive:
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
m_doubleSided = false;
break;
default:
glDisable(GL_CULL_FACE);
m_doubleSided = true; // basically drawing on both sides now
}
}
m_matDet = test;
m_dirtyModel = false;
}
} // New3D } // New3D

View file

@ -11,33 +11,43 @@ class R3DShader
public: public:
R3DShader(); R3DShader();
bool LoadShader(const char* vertexShader = nullptr, const char* fragmentShader = nullptr); bool LoadShader (const char* vertexShader = nullptr, const char* fragmentShader = nullptr);
void SetMeshUniforms(const Mesh* m); void SetMeshUniforms (const Mesh* m);
void SetViewportUniforms(const Viewport *vp); void SetModelStates (const Model* model);
void Start(); void SetViewportUniforms (const Viewport *vp);
void SetShader(bool enable = true); void Start ();
void SetShader (bool enable = true);
private: private:
// shader IDs
GLuint m_shaderProgram; GLuint m_shaderProgram;
GLuint m_vertexShader; GLuint m_vertexShader;
GLuint m_fragmentShader; GLuint m_fragmentShader;
// mesh uniform data // mesh uniform locations
GLint m_locTexture; GLint m_locTexture;
GLint m_locTextureEnabled; GLint m_locTextureEnabled;
GLint m_locTextureAlpha; GLint m_locTextureAlpha;
GLint m_locAlphaTest; GLint m_locAlphaTest;
bool m_textured; // cached mesh values
bool m_textureAlpha; // use alpha in texture bool m_textured;
bool m_alphaTest; // discard fragment based on alpha (ogl does this with fixed function) bool m_textureAlpha; // use alpha in texture
float m_fogIntensity; bool m_alphaTest; // discard fragment based on alpha (ogl does this with fixed function)
bool m_doubleSided; float m_fogIntensity;
bool m_doubleSided;
bool m_lightEnabled;
bool m_dirty; // cached model values
enum class MatDet { notset, negative, positive, zero };
MatDet m_matDet;
// viewport uniform data // are our cache values dirty
bool m_dirtyMesh;
bool m_dirtyModel;
// viewport uniform locations
GLint m_locFogIntensity; GLint m_locFogIntensity;
GLint m_locFogDensity; GLint m_locFogDensity;
GLint m_locFogStart; GLint m_locFogStart;