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
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
int lutIdx = 0;

View file

@ -83,25 +83,26 @@ void CNew3D::RenderScene(int priority, bool alpha)
for (auto &n : m_nodes) {
if (n.viewport.priority != priority) {
if (n.viewport.priority != priority || n.models.empty()) {
continue;
}
glViewport(n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(n.viewport.projectionMatrix);
glViewport (n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height);
glMatrixMode (GL_PROJECTION);
glLoadMatrixf (n.viewport.projectionMatrix);
glMatrixMode (GL_MODELVIEW);
m_r3dShader.SetViewportUniforms(&n.viewport);
for (auto &m : n.models) {
bool matrixLoaded = false;
if (m.meshes.empty()) {
continue;
}
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(m.modelMat);
m_r3dShader.SetModelStates(&m);
for (auto &mesh : m.meshes) {
@ -115,6 +116,11 @@ void CNew3D::RenderScene(int priority, bool alpha)
continue;
}
}
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) {
mesh.texture->BindTexture();
@ -244,6 +250,9 @@ bool CNew3D::DrawModel(UINT32 modelAddr)
m->modelMat[i] = m_modelMat.currentMatrix[i];
}
//calculate determinant
m->determinant = Determinant3x3(m_modelMat);
CacheModel(m, modelAddress);
return true;
@ -706,15 +715,13 @@ void CNew3D::CopyVertexData(R3DPoly& r3dPoly, std::vector<Poly>& polyArray)
Poly p;
V3::Vec3 normal;
float dotProd;
float zFlip;
bool clockWise;
//====================
V3::createNormal(r3dPoly.v[0].pos, r3dPoly.v[1].pos, r3dPoly.v[2].pos, normal);
dotProd = V3::dotProduct(normal, r3dPoly.faceNormal);
zFlip = -1.0f*m_matrixBasePtr[0x5]; // coordinate system m13 component
clockWise = (zFlip*dotProd >= 0.0);
clockWise = dotProd >= 0.0;
if (clockWise) {
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
// 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]) {
/*

View file

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

View file

@ -143,8 +143,12 @@ void R3DShader::Start()
m_textureAlpha = false; // use alpha in texture
m_alphaTest = false; // discard fragment based on alpha (ogl does this with fixed function)
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)
@ -206,52 +210,61 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
return; // sanity check
}
if (m_dirty) {
if (m_dirtyMesh) {
glUniform1i(m_locTexture, 0);
}
if (m_dirty || m->textured != m_textured) {
if (m_dirtyMesh || m->textured != m_textured) {
glUniform1i(m_locTextureEnabled, 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);
m_alphaTest = m->alphaTest;
}
if (m_dirty || m->textureAlpha != m_textureAlpha) {
if (m_dirtyMesh || m->textureAlpha != m_textureAlpha) {
glUniform1i(m_locTextureAlpha, 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);
m_fogIntensity = m->fogIntensity;
}
glUniform1i(m_locLightEnable, m->lighting);
glUniform1f(m_locShininess, 1);
if (m_dirtyMesh || m->lighting != m_lightEnabled) {
glUniform1i(m_locLightEnable, m->lighting);
m_lightEnabled = m->lighting;
}
// technically not uniforms
if (m_dirty || m->doubleSided != m_doubleSided) {
m_doubleSided = m->doubleSided;
if (m_doubleSided) {
glDisable(GL_CULL_FACE);
}
else {
glEnable(GL_CULL_FACE);
//glUniform1f(m_locShininess, 1);
if (m_matDet!=MatDet::zero) {
if (m_dirtyMesh || m->doubleSided != m_doubleSided) {
m_doubleSided = m->doubleSided;
if (m_doubleSided) {
glDisable(GL_CULL_FACE);
}
else {
glEnable(GL_CULL_FACE);
}
}
}
m_dirty = false;
m_dirtyMesh = false;
}
void R3DShader::SetViewportUniforms(const Viewport *vp)
{
//didn't bother caching these, they don't get frequently called anyway
glUniform1f(m_locFogDensity, vp->fogParams[3]);
glUniform1f(m_locFogStart, vp->fogParams[4]);
glUniform1f (m_locFogDensity, vp->fogParams[3]);
glUniform1f (m_locFogStart, vp->fogParams[4]);
glUniform3fv(m_locFogColour, 1, vp->fogParams);
glUniform3fv(m_locLighting, 2, vp->lightingParams);
@ -260,4 +273,37 @@ void R3DShader::SetViewportUniforms(const Viewport *vp)
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

View file

@ -11,33 +11,43 @@ class R3DShader
public:
R3DShader();
bool LoadShader(const char* vertexShader = nullptr, const char* fragmentShader = nullptr);
void SetMeshUniforms(const Mesh* m);
void SetViewportUniforms(const Viewport *vp);
void Start();
void SetShader(bool enable = true);
bool LoadShader (const char* vertexShader = nullptr, const char* fragmentShader = nullptr);
void SetMeshUniforms (const Mesh* m);
void SetModelStates (const Model* model);
void SetViewportUniforms (const Viewport *vp);
void Start ();
void SetShader (bool enable = true);
private:
// shader IDs
GLuint m_shaderProgram;
GLuint m_vertexShader;
GLuint m_fragmentShader;
// mesh uniform data
// mesh uniform locations
GLint m_locTexture;
GLint m_locTextureEnabled;
GLint m_locTextureAlpha;
GLint m_locAlphaTest;
bool m_textured;
bool m_textureAlpha; // use alpha in texture
bool m_alphaTest; // discard fragment based on alpha (ogl does this with fixed function)
float m_fogIntensity;
bool m_doubleSided;
// cached mesh values
bool m_textured;
bool m_textureAlpha; // use alpha in texture
bool m_alphaTest; // discard fragment based on alpha (ogl does this with fixed function)
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_locFogDensity;
GLint m_locFogStart;