mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-26 07:35:40 +00:00
Modern hardware does backface culling in window space by calculating the face normal for the polygon, then doing a dot product against the view vector. The real3d pro-1000 on the other hand passes a pre-calculated face normal for each polygon which is used for culling. We were using this face normal to rewind the polygons so that regular backface culling would work. This worked 99.9% of the time. However this was failing on some models in Virtua Striker. The reason was because the pre-calculated face normals being passed were actually completely different to the actual face normals for the poly (not just inverted like you would expect). This broke our code. The solution was to emulate face culling directly in the vertex shader using the pre-calculated face normals directly. Only minimally tested this but hopefully there are no obvious regressions.
This commit is contained in:
parent
f04a285727
commit
165926aa06
|
@ -27,6 +27,7 @@ struct Vertex
|
||||||
float normal[3];
|
float normal[3];
|
||||||
float texcoords[2];
|
float texcoords[2];
|
||||||
UINT8 color[4];
|
UINT8 color[4];
|
||||||
|
float faceNormal[3];
|
||||||
float fixedShade;
|
float fixedShade;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -119,7 +120,6 @@ 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
|
|
||||||
|
|
||||||
//model scale step 1.5+
|
//model scale step 1.5+
|
||||||
float scale = 1.0f;
|
float scale = 1.0f;
|
||||||
|
|
|
@ -250,7 +250,7 @@ void CNew3D::RenderFrame(void)
|
||||||
glEnable (GL_DEPTH_TEST);
|
glEnable (GL_DEPTH_TEST);
|
||||||
glDepthMask (GL_TRUE);
|
glDepthMask (GL_TRUE);
|
||||||
glActiveTexture (GL_TEXTURE0);
|
glActiveTexture (GL_TEXTURE0);
|
||||||
glEnable (GL_CULL_FACE);
|
glDisable (GL_CULL_FACE); // we'll emulate this in the shader
|
||||||
glFrontFace (GL_CW);
|
glFrontFace (GL_CW);
|
||||||
|
|
||||||
glStencilFunc (GL_EQUAL, 0, 0xFF); // basically stencil test passes if the value is zero
|
glStencilFunc (GL_EQUAL, 0, 0xFF); // basically stencil test passes if the value is zero
|
||||||
|
@ -286,12 +286,14 @@ void CNew3D::RenderFrame(void)
|
||||||
glEnableVertexAttribArray(2);
|
glEnableVertexAttribArray(2);
|
||||||
glEnableVertexAttribArray(3);
|
glEnableVertexAttribArray(3);
|
||||||
glEnableVertexAttribArray(4);
|
glEnableVertexAttribArray(4);
|
||||||
|
glEnableVertexAttribArray(5);
|
||||||
|
|
||||||
// before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil ..
|
// before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil ..
|
||||||
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inVertex"), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
|
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inVertex"), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
|
||||||
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
|
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
|
||||||
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inTexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoords));
|
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inTexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoords));
|
||||||
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inColour"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color));
|
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inColour"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color));
|
||||||
|
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFaceNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, faceNormal));
|
||||||
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFixedShade"), 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, fixedShade));
|
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFixedShade"), 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, fixedShade));
|
||||||
|
|
||||||
m_r3dShader.SetShader(true);
|
m_r3dShader.SetShader(true);
|
||||||
|
@ -321,12 +323,13 @@ void CNew3D::RenderFrame(void)
|
||||||
m_vbo.Bind(false);
|
m_vbo.Bind(false);
|
||||||
|
|
||||||
glDisable(GL_STENCIL_TEST);
|
glDisable(GL_STENCIL_TEST);
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
glDisableVertexAttribArray(0);
|
glDisableVertexAttribArray(0);
|
||||||
glDisableVertexAttribArray(1);
|
glDisableVertexAttribArray(1);
|
||||||
glDisableVertexAttribArray(2);
|
glDisableVertexAttribArray(2);
|
||||||
glDisableVertexAttribArray(3);
|
glDisableVertexAttribArray(3);
|
||||||
glDisableVertexAttribArray(4);
|
glDisableVertexAttribArray(4);
|
||||||
|
glDisableVertexAttribArray(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNew3D::BeginFrame(void)
|
void CNew3D::BeginFrame(void)
|
||||||
|
@ -410,9 +413,6 @@ 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);
|
|
||||||
|
|
||||||
// update texture offsets
|
// update texture offsets
|
||||||
m->textureOffsetX = m_nodeAttribs.currentTexOffsetX;
|
m->textureOffsetX = m_nodeAttribs.currentTexOffsetX;
|
||||||
m->textureOffsetY = m_nodeAttribs.currentTexOffsetY;
|
m->textureOffsetY = m_nodeAttribs.currentTexOffsetY;
|
||||||
|
@ -897,28 +897,11 @@ void CNew3D::RenderViewport(UINT32 addr)
|
||||||
|
|
||||||
void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray)
|
void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray)
|
||||||
{
|
{
|
||||||
//====================
|
|
||||||
Poly p;
|
Poly p;
|
||||||
V3::Vec3 normal;
|
|
||||||
float dotProd;
|
|
||||||
bool clockWise;
|
|
||||||
//====================
|
|
||||||
|
|
||||||
V3::createNormal(r3dPoly.v[0].pos, r3dPoly.v[1].pos, r3dPoly.v[2].pos, normal);
|
|
||||||
|
|
||||||
dotProd = V3::dotProduct(normal, r3dPoly.faceNormal);
|
|
||||||
clockWise = dotProd >= 0;
|
|
||||||
|
|
||||||
if (clockWise) {
|
|
||||||
p.p1 = r3dPoly.v[0];
|
p.p1 = r3dPoly.v[0];
|
||||||
p.p2 = r3dPoly.v[1];
|
p.p2 = r3dPoly.v[1];
|
||||||
p.p3 = r3dPoly.v[2];
|
p.p3 = r3dPoly.v[2];
|
||||||
}
|
|
||||||
else {
|
|
||||||
p.p1 = r3dPoly.v[2];
|
|
||||||
p.p2 = r3dPoly.v[1];
|
|
||||||
p.p3 = r3dPoly.v[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy face colour to vertices
|
// Copy face colour to vertices
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
|
@ -927,25 +910,20 @@ void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray
|
||||||
p.p3.color[i] = r3dPoly.faceColour[i];
|
p.p3.color[i] = r3dPoly.faceColour[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy face normal
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
p.p1.faceNormal[i] = r3dPoly.faceNormal[i];
|
||||||
|
p.p2.faceNormal[i] = r3dPoly.faceNormal[i];
|
||||||
|
p.p3.faceNormal[i] = r3dPoly.faceNormal[i];
|
||||||
|
}
|
||||||
|
|
||||||
polyArray.emplace_back(p);
|
polyArray.emplace_back(p);
|
||||||
|
|
||||||
if (r3dPoly.number == 4) {
|
if (r3dPoly.number == 4) {
|
||||||
|
|
||||||
V3::createNormal(r3dPoly.v[0].pos, r3dPoly.v[2].pos, r3dPoly.v[3].pos, normal);
|
|
||||||
|
|
||||||
dotProd = V3::dotProduct(normal, r3dPoly.faceNormal);
|
|
||||||
clockWise = dotProd >= 0;
|
|
||||||
|
|
||||||
if (clockWise) {
|
|
||||||
p.p1 = r3dPoly.v[0];
|
p.p1 = r3dPoly.v[0];
|
||||||
p.p2 = r3dPoly.v[2];
|
p.p2 = r3dPoly.v[2];
|
||||||
p.p3 = r3dPoly.v[3];
|
p.p3 = r3dPoly.v[3];
|
||||||
}
|
|
||||||
else {
|
|
||||||
p.p1 = r3dPoly.v[0];
|
|
||||||
p.p2 = r3dPoly.v[3];
|
|
||||||
p.p3 = r3dPoly.v[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy face colour to vertices
|
// Copy face colour to vertices
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
|
@ -954,6 +932,13 @@ void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray
|
||||||
p.p3.color[i] = r3dPoly.faceColour[i];
|
p.p3.color[i] = r3dPoly.faceColour[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy face normal
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
p.p1.faceNormal[i] = r3dPoly.faceNormal[i];
|
||||||
|
p.p2.faceNormal[i] = r3dPoly.faceNormal[i];
|
||||||
|
p.p3.faceNormal[i] = r3dPoly.faceNormal[i];
|
||||||
|
}
|
||||||
|
|
||||||
polyArray.emplace_back(p);
|
polyArray.emplace_back(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ attribute vec4 inVertex;
|
||||||
attribute vec3 inNormal;
|
attribute vec3 inNormal;
|
||||||
attribute vec2 inTexCoord;
|
attribute vec2 inTexCoord;
|
||||||
attribute vec4 inColour;
|
attribute vec4 inColour;
|
||||||
|
attribute vec3 inFaceNormal; // used to emulate r3d culling
|
||||||
attribute float inFixedShade;
|
attribute float inFixedShade;
|
||||||
|
|
||||||
// outputs to fragment shader
|
// outputs to fragment shader
|
||||||
|
@ -26,8 +27,18 @@ varying vec3 fsViewVertex;
|
||||||
varying vec3 fsViewNormal; // per vertex normal vector
|
varying vec3 fsViewNormal; // per vertex normal vector
|
||||||
varying vec2 fsTexCoord;
|
varying vec2 fsTexCoord;
|
||||||
varying vec4 fsColor;
|
varying vec4 fsColor;
|
||||||
|
varying float fsDiscard; // can't have varying bool (glsl spec)
|
||||||
varying float fsFixedShade;
|
varying float fsFixedShade;
|
||||||
|
|
||||||
|
float CalcBackFace(in vec3 viewVertex)
|
||||||
|
{
|
||||||
|
vec3 vt = viewVertex - vec3(0.0);
|
||||||
|
vec3 vn = (mat3(gl_ModelViewMatrix) * inFaceNormal);
|
||||||
|
|
||||||
|
// dot product of face normal with view direction
|
||||||
|
return dot(vt, vn);
|
||||||
|
}
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
fsViewVertex = vec3(gl_ModelViewMatrix * inVertex);
|
fsViewVertex = vec3(gl_ModelViewMatrix * inVertex);
|
||||||
|
@ -35,6 +46,7 @@ void main(void)
|
||||||
float z = length(fsViewVertex);
|
float z = length(fsViewVertex);
|
||||||
fsFogFactor = fogIntensity * clamp(fogStart + z * fogDensity, 0.0, 1.0);
|
fsFogFactor = fogIntensity * clamp(fogStart + z * fogDensity, 0.0, 1.0);
|
||||||
|
|
||||||
|
fsDiscard = CalcBackFace(fsViewVertex);
|
||||||
fsColor = inColour;
|
fsColor = inColour;
|
||||||
fsTexCoord = inTexCoord;
|
fsTexCoord = inTexCoord;
|
||||||
fsFixedShade = inFixedShade;
|
fsFixedShade = inFixedShade;
|
||||||
|
@ -82,6 +94,7 @@ varying vec3 fsViewVertex;
|
||||||
varying vec3 fsViewNormal; // per vertex normal vector
|
varying vec3 fsViewNormal; // per vertex normal vector
|
||||||
varying vec4 fsColor;
|
varying vec4 fsColor;
|
||||||
varying vec2 fsTexCoord;
|
varying vec2 fsTexCoord;
|
||||||
|
varying float fsDiscard;
|
||||||
varying float fsFixedShade;
|
varying float fsFixedShade;
|
||||||
|
|
||||||
vec4 GetTextureValue()
|
vec4 GetTextureValue()
|
||||||
|
@ -129,6 +142,10 @@ void main()
|
||||||
vec4 finalData;
|
vec4 finalData;
|
||||||
vec4 fogData;
|
vec4 fogData;
|
||||||
|
|
||||||
|
if(fsDiscard>0) {
|
||||||
|
discard; //emulate back face culling here
|
||||||
|
}
|
||||||
|
|
||||||
fogData = vec4(fogColour.rgb * fogAmbient, fsFogFactor);
|
fogData = vec4(fogColour.rgb * fogAmbient, fsFogFactor);
|
||||||
tex1Data = vec4(1.0, 1.0, 1.0, 1.0);
|
tex1Data = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
@ -261,7 +278,6 @@ void R3DShader::Start()
|
||||||
m_textured2 = false;
|
m_textured2 = false;
|
||||||
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_lightEnabled = false;
|
m_lightEnabled = false;
|
||||||
m_specularEnabled = false;
|
m_specularEnabled = false;
|
||||||
m_layered = false;
|
m_layered = false;
|
||||||
|
@ -275,8 +291,6 @@ void R3DShader::Start()
|
||||||
m_baseTexSize[0] = 0;
|
m_baseTexSize[0] = 0;
|
||||||
m_baseTexSize[1] = 0;
|
m_baseTexSize[1] = 0;
|
||||||
|
|
||||||
m_matDet = MatDet::notset;
|
|
||||||
|
|
||||||
m_dirtyMesh = true; // dirty means all the above are dirty, ie first run
|
m_dirtyMesh = true; // dirty means all the above are dirty, ie first run
|
||||||
m_dirtyModel = true;
|
m_dirtyModel = true;
|
||||||
}
|
}
|
||||||
|
@ -443,22 +457,6 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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_dirtyMesh = false;
|
m_dirtyMesh = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,41 +482,11 @@ void R3DShader::SetViewportUniforms(const Viewport *vp)
|
||||||
|
|
||||||
void R3DShader::SetModelStates(const Model* model)
|
void R3DShader::SetModelStates(const Model* model)
|
||||||
{
|
{
|
||||||
//==========
|
|
||||||
MatDet test;
|
|
||||||
//==========
|
|
||||||
|
|
||||||
test = MatDet::notset; // happens for bad matrices with NaN
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_dirtyModel || model->scale != m_modelScale) {
|
if (m_dirtyModel || model->scale != m_modelScale) {
|
||||||
glUniform1f(m_locModelScale, model->scale);
|
glUniform1f(m_locModelScale, model->scale);
|
||||||
m_modelScale = model->scale;
|
m_modelScale = model->scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_matDet = test;
|
|
||||||
m_dirtyModel = false;
|
m_dirtyModel = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ private:
|
||||||
bool m_textureAlpha; // use alpha in texture
|
bool m_textureAlpha; // use alpha in texture
|
||||||
bool m_alphaTest; // discard fragment based on alpha (ogl does this with fixed function)
|
bool m_alphaTest; // discard fragment based on alpha (ogl does this with fixed function)
|
||||||
float m_fogIntensity;
|
float m_fogIntensity;
|
||||||
bool m_doubleSided;
|
|
||||||
bool m_lightEnabled;
|
bool m_lightEnabled;
|
||||||
float m_shininess;
|
float m_shininess;
|
||||||
float m_specularValue;
|
float m_specularValue;
|
||||||
|
@ -60,8 +59,6 @@ private:
|
||||||
bool m_textureInverted;
|
bool m_textureInverted;
|
||||||
|
|
||||||
// cached model values
|
// cached model values
|
||||||
enum class MatDet { notset, negative, positive, zero };
|
|
||||||
MatDet m_matDet;
|
|
||||||
float m_modelScale;
|
float m_modelScale;
|
||||||
|
|
||||||
// are our cache values dirty
|
// are our cache values dirty
|
||||||
|
|
Loading…
Reference in a new issue