mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-04-10 19:15:14 +00:00
finish front/back face culling code based on matrix determinant. Optimised opengl to avoid redundant state changes.
This commit is contained in:
parent
44ea902181
commit
f031e5d095
|
@ -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;
|
||||||
|
|
|
@ -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]) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue